2 changed files with 807 additions and 3 deletions
@ -0,0 +1,807 @@
@@ -0,0 +1,807 @@
|
||||
NIP-XX |
||||
====== |
||||
|
||||
Decentralized Name Registry with Trust-Weighted Consensus |
||||
---------------------------------------------------------- |
||||
|
||||
`draft` `optional` |
||||
|
||||
## Abstract |
||||
|
||||
This NIP defines a decentralized name registry protocol for human-readable resource naming with trustless title transfer. The protocol uses trust-weighted attestations from relay operators to achieve Byzantine fault tolerance without requiring centralized coordination or proof-of-work. Consensus is reached through social mechanisms of association and affinity, enabling the network to scale to thousands of participating relays while maintaining ~51% Byzantine fault tolerance against censorship. |
||||
|
||||
## Motivation |
||||
|
||||
Many peer-to-peer applications require human-readable naming systems for locating network resources (analogous to DNS). Traditional approaches either rely on centralized authorities or blockchain-based systems with slow finality times and high resource requirements. |
||||
|
||||
This proposal leverages Nostr's existing social graph and relay infrastructure to create a naming system that is: |
||||
|
||||
- **Trustless**: No single authority controls name registration |
||||
- **Byzantine fault tolerant**: Resistant to malicious relays (up to ~51% by trust weight) |
||||
- **Scalable**: Supports thousands of participating relays |
||||
- **Censorship resistant**: Diverse trust graphs make network-wide censorship difficult |
||||
- **Permissionless**: Anyone can operate a relay and participate in consensus |
||||
|
||||
The protocol achieves finality in 1-2 minutes, which is acceptable for DNS-like use cases while avoiding the complexity and resource requirements of traditional blockchain consensus. |
||||
|
||||
## Specification |
||||
|
||||
### Event Kinds |
||||
|
||||
This NIP defines the following event kinds: |
||||
|
||||
- `30100`: Registration Proposal - Claim or transfer of a name (parameterized replaceable) |
||||
- `20100`: Attestation - Relay operator's vote on a registration proposal (ephemeral) |
||||
- `30101`: Trust Graph - Relay's trust relationships with other relays (parameterized replaceable) |
||||
- `30102`: Name State - Current ownership state (parameterized replaceable) |
||||
|
||||
### Registration Proposal (Kind 30100) |
||||
|
||||
A parameterized replaceable event where users propose to register or transfer a name: |
||||
|
||||
```json |
||||
{ |
||||
"kind": 30100, |
||||
"pubkey": "<claimant_pubkey>", |
||||
"created_at": <unix_timestamp>, |
||||
"tags": [ |
||||
["d", "<name>"], // name being claimed (e.g., "foo.n") |
||||
["action", "register"], // "register" or "transfer" |
||||
["prev_owner", "<pubkey>"], // previous owner pubkey (for transfers only) |
||||
["prev_sig", "<signature>"] // signature from prev_owner authorizing transfer |
||||
], |
||||
"content": "", |
||||
"sig": "<signature>" |
||||
} |
||||
``` |
||||
|
||||
**Field Specifications:** |
||||
|
||||
- `d` tag: The name being registered. MUST be unique within the namespace. |
||||
- `action` tag: Either `register` (initial claim) or `transfer` (ownership change) |
||||
- `prev_owner` tag: Required for `transfer` actions. The pubkey of the current owner. |
||||
- `prev_sig` tag: Required for `transfer` actions. Signature proving authorization from previous owner. |
||||
|
||||
**Transfer Authorization:** |
||||
|
||||
For transfers, the `prev_sig` MUST be a signature over the following message: |
||||
``` |
||||
transfer:<name>:<new_owner_pubkey>:<timestamp> |
||||
``` |
||||
|
||||
This prevents unauthorized transfers and ensures the current owner explicitly approves the transfer. |
||||
|
||||
### Attestation (Kind 20100) |
||||
|
||||
An ephemeral event where relay operators attest to their acceptance or rejection of a registration proposal: |
||||
|
||||
```json |
||||
{ |
||||
"kind": 20100, |
||||
"pubkey": "<relay_operator_pubkey>", |
||||
"created_at": <unix_timestamp>, |
||||
"tags": [ |
||||
["e", "<proposal_event_id>"], // the registration proposal being attested |
||||
["decision", "approve"], // "approve", "reject", or "abstain" |
||||
["weight", "100"], // optional stake/confidence weight |
||||
["reason", "first_valid"], // optional audit trail |
||||
["relay", "wss://relay.example.com"] // the relay this operator controls |
||||
], |
||||
"content": "", |
||||
"sig": "<signature>" |
||||
} |
||||
``` |
||||
|
||||
**Field Specifications:** |
||||
|
||||
- `e` tag: References the registration proposal event ID |
||||
- `decision` tag: One of: |
||||
- `approve`: Relay accepts this registration as valid |
||||
- `reject`: Relay rejects this registration (e.g., conflict detected) |
||||
- `abstain`: Relay acknowledges but doesn't vote |
||||
- `weight` tag: Optional numeric weight (default: 100). Higher weights indicate stronger confidence or stake. |
||||
- `reason` tag: Optional human-readable justification for audit trails |
||||
- `relay` tag: WebSocket URL of the relay this operator controls |
||||
|
||||
**Attestation Window:** |
||||
|
||||
Relays SHOULD publish attestations within 60-120 seconds of receiving a registration proposal. This allows sufficient time for gossip propagation while maintaining reasonable finality times. |
||||
|
||||
### Trust Graph (Kind 30101) |
||||
|
||||
A parameterized replaceable event where relay operators declare their trust relationships: |
||||
|
||||
```json |
||||
{ |
||||
"kind": 30101, |
||||
"pubkey": "<relay_operator_pubkey>", |
||||
"created_at": <unix_timestamp>, |
||||
"tags": [ |
||||
["d", "trust-graph"], // identifier for replacement |
||||
["p", "<trusted_relay1_pubkey>", "wss://relay1.com", "0.9"], |
||||
["p", "<trusted_relay2_pubkey>", "wss://relay2.com", "0.7"], |
||||
["p", "<trusted_relay3_pubkey>", "wss://relay3.com", "0.5"] |
||||
], |
||||
"content": "", |
||||
"sig": "<signature>" |
||||
} |
||||
``` |
||||
|
||||
**Field Specifications:** |
||||
|
||||
- `p` tag: Defines trust in another relay operator |
||||
- First parameter: Trusted relay operator's pubkey |
||||
- Second parameter: Relay WebSocket URL |
||||
- Third parameter: Trust score (0.0 to 1.0, where 1.0 = complete trust) |
||||
|
||||
**Trust Score Guidelines:** |
||||
|
||||
- `1.0`: Complete trust (e.g., operator's own other relays) |
||||
- `0.7-0.9`: High trust (well-known, reputable operators) |
||||
- `0.4-0.6`: Moderate trust (established but less familiar) |
||||
- `0.1-0.3`: Low trust (new or unknown operators) |
||||
- `0.0`: No trust (excluded from consensus) |
||||
|
||||
**Trust Graph Updates:** |
||||
|
||||
Relay operators SHOULD update their trust graphs gradually. Rapid changes to trust relationships MAY be penalized in weighted consensus calculations to prevent manipulation. |
||||
|
||||
### Name State (Kind 30102) |
||||
|
||||
A parameterized replaceable event representing the current ownership state of a name: |
||||
|
||||
```json |
||||
{ |
||||
"kind": 30102, |
||||
"pubkey": "<relay_operator_pubkey>", |
||||
"created_at": <unix_timestamp>, |
||||
"tags": [ |
||||
["d", "<name>"], // the name |
||||
["owner", "<current_owner_pubkey>"], |
||||
["registered_at", "<timestamp>"], |
||||
["proposal", "<proposal_event_id>"], |
||||
["attestations", "<count>"], |
||||
["confidence", "0.87"] // consensus confidence score |
||||
], |
||||
"content": "<optional_metadata_json>", |
||||
"sig": "<signature>" |
||||
} |
||||
``` |
||||
|
||||
This event is published by relays after consensus is reached, allowing clients to quickly query current ownership state without recomputing consensus. |
||||
|
||||
### Consensus Algorithm |
||||
|
||||
Each relay independently computes consensus using the following algorithm: |
||||
|
||||
#### 1. Attestation Window |
||||
|
||||
When a relay receives a registration proposal for name `N`: |
||||
|
||||
1. Start a timer for `T` seconds (recommended: 60-120s) |
||||
2. Collect all attestations for this proposal |
||||
3. Collect competing proposals for the same name `N` |
||||
|
||||
#### 2. Trust-Weighted Scoring |
||||
|
||||
For each competing proposal `P`, compute a weighted score: |
||||
|
||||
``` |
||||
score(P) = Σ (attestation_weight × trust_decay) |
||||
``` |
||||
|
||||
Where: |
||||
- `attestation_weight`: The weight from the attestation's `weight` tag (default: 100) |
||||
- `trust_decay`: Distance-based trust decay from this relay's trust graph: |
||||
- **Direct trust** (0-hop): `trust_score × 1.0` |
||||
- **1-hop trust**: `trust_score × 0.8` |
||||
- **2-hop trust**: `trust_score × 0.6` |
||||
- **3-hop trust**: `trust_score × 0.4` |
||||
- **4+ hops**: `0.0` (not counted) |
||||
|
||||
#### 3. Trust Distance Calculation |
||||
|
||||
Trust distance is computed using the relay's trust graph (kind 30101 events): |
||||
|
||||
1. Build a directed graph from all relay trust declarations |
||||
2. Use Dijkstra's algorithm or breadth-first search to find shortest trust path |
||||
3. Multiply trust scores along the path for effective trust weight |
||||
|
||||
**Example:** |
||||
``` |
||||
Relay A → Relay B (0.9) → Relay C (0.8) |
||||
Effective trust from A to C: 0.9 × 0.8 × 0.8 (1-hop decay) = 0.576 |
||||
``` |
||||
|
||||
#### 4. Consensus Decision |
||||
|
||||
After the attestation window expires: |
||||
|
||||
1. **Compute total trust weight**: Sum of all attestation scores across all proposals |
||||
2. **Compute proposal score**: Each proposal's weighted attestation sum |
||||
3. **Accept proposal `P` if:** |
||||
- `score(P) / total_trust_weight > threshold` (recommended: 0.51) |
||||
- No competing proposal has higher score |
||||
- Valid transfer authorization (if action is "transfer") |
||||
|
||||
4. **Defer decision if:** |
||||
- No proposal reaches threshold |
||||
- Multiple proposals exceed threshold with similar scores (within 5%) |
||||
- Insufficient attestations received (coverage < 30% of trust graph) |
||||
|
||||
5. **Publish name state** (kind 30102) once consensus is reached |
||||
|
||||
### Sparse Attestation Optimization |
||||
|
||||
To reduce network load, relays MAY use sparse attestation where they only attest when: |
||||
|
||||
1. **Local interest**: One of the relay's connected clients queries this name |
||||
2. **Duty rotation**: `hash(proposal_event_id || relay_pubkey) % K == 0` (for sampling rate 1/K) |
||||
3. **Conflict detected**: Multiple competing proposals received for the same name |
||||
4. **Direct request**: Client explicitly requests attestation |
||||
|
||||
**Recommended sampling rates:** |
||||
- Networks with <100 relays: K=1 (100% attestation) |
||||
- Networks with 100-1000 relays: K=10 (10% attestation) |
||||
- Networks with >1000 relays: K=20 (5% attestation) |
||||
|
||||
This optimization reduces attestation volume by 80-95% while maintaining consensus reliability through randomized duty rotation. |
||||
|
||||
### Handling Conflicts |
||||
|
||||
#### Simultaneous Registration |
||||
|
||||
When multiple proposals for the same name arrive at similar timestamps: |
||||
|
||||
1. **Primary resolution**: Weighted consensus (highest score wins) |
||||
2. **Tiebreaker**: If scores are within 5%, use lexicographic ordering of proposal event IDs |
||||
3. **Client notification**: Relays SHOULD publish notices about conflicts for transparency |
||||
|
||||
#### Competing Transfers |
||||
|
||||
When multiple transfer proposals claim to originate from the same owner: |
||||
|
||||
1. Verify `prev_sig` authenticity for each proposal |
||||
2. Accept the earliest valid transfer (by `created_at`) |
||||
3. If timestamps are identical (within 1 second), use event ID tiebreaker |
||||
|
||||
#### Trust Graph Divergence |
||||
|
||||
Different relays may reach different consensus due to divergent trust graphs. This is acceptable and provides censorship resistance: |
||||
|
||||
- Clients SHOULD query multiple relays (recommended: 3-5) |
||||
- Use majority consensus among queried relays |
||||
- Display uncertainty warnings if relays disagree (similar to SSL certificate warnings) |
||||
|
||||
### Finality Timeline |
||||
|
||||
Expected timeline for name registration: |
||||
|
||||
``` |
||||
T=0s: Registration proposal published |
||||
T=0-30s: Proposal propagates via Nostr gossip |
||||
T=30-90s: Relays publish attestations |
||||
T=90s: Most relays compute weighted consensus |
||||
T=120s: High confidence threshold (2-minute finality) |
||||
``` |
||||
|
||||
**Early finality**: If >70% of expected attestations arrive within 30s and reach consensus threshold, relays MAY finalize earlier. |
||||
|
||||
## Implementation Guidelines |
||||
|
||||
### Client Implementation |
||||
|
||||
Clients querying name ownership should: |
||||
|
||||
1. Subscribe to kind 30102 events for the name from multiple relays |
||||
2. Use majority consensus among responses |
||||
3. Warn users if relays disagree on ownership |
||||
4. Cache results with TTL of 5-10 minutes |
||||
5. Re-query on cache expiry or when name is used in critical operations |
||||
|
||||
### Relay Implementation |
||||
|
||||
Relays participating in consensus should: |
||||
|
||||
1. Subscribe to kind 30100, 20100, and 30101 events from trusted relays |
||||
2. Maintain local trust graph (derived from kind 30101 events) |
||||
3. Implement the consensus algorithm described above |
||||
4. Publish attestations (kind 20100) for proposals of interest |
||||
5. Publish name state (kind 30102) after reaching consensus |
||||
6. Prune expired ephemeral attestations (>7 days old) |
||||
|
||||
**Storage requirements:** |
||||
- Trust graph: ~100 KB per 1000 relays |
||||
- Active proposals: ~1 MB per 1000 pending registrations |
||||
- Attestations (7-day window): ~100 MB per 1000 relays at 1000 registrations/day |
||||
- Name state: ~1 KB per name |
||||
|
||||
### Bootstrap Relay Discovery |
||||
|
||||
New relays should bootstrap their trust graph by: |
||||
|
||||
1. Connecting to well-known seed relays (similar to NIP-65 relay lists) |
||||
2. Importing trust graphs from 3-5 reputable relays |
||||
3. Using weighted average of imported trust scores as initial state |
||||
4. Gradually adjusting trust based on observed relay behavior over 30 days |
||||
|
||||
## Security Considerations |
||||
|
||||
### Attack Vectors and Mitigations |
||||
|
||||
#### 1. Sybil Attack |
||||
|
||||
**Attack**: Attacker creates thousands of relay identities to dominate attestations. |
||||
|
||||
**Mitigation**: |
||||
- Trust-weighted consensus prevents new relays from having immediate influence |
||||
- Established relays must trust new relays before they gain weight |
||||
- Age-weighted trust (new relays have reduced influence for 30 days) |
||||
|
||||
#### 2. Trust Graph Manipulation |
||||
|
||||
**Attack**: Attacker gradually builds trust relationships then exploits them. |
||||
|
||||
**Mitigation**: |
||||
- Audit trails (kind 30101 events are public and verifiable) |
||||
- Sudden trust changes are penalized in consensus calculations |
||||
- Diverse trust graphs mean attacker must control different sets of relays for different victims |
||||
|
||||
#### 3. Censorship |
||||
|
||||
**Attack**: Powerful relays refuse to attest certain names (e.g., dissidents, competitors). |
||||
|
||||
**Mitigation**: |
||||
- Subjective consensus means each relay has its own trust graph |
||||
- Censoring a name requires controlling >51% of *each relay's* trust graph |
||||
- More difficult than controlling 51% of network-wide consensus |
||||
- Users can choose relays aligned with their values |
||||
|
||||
#### 4. Name Squatting |
||||
|
||||
**Attack**: Registering valuable names without use. |
||||
|
||||
**Mitigation** (optional extensions): |
||||
- Name expiration: Require periodic renewal (NIP extension) |
||||
- Economic cost: Require payment or proof-of-burn for registration |
||||
- Proof-of-use: Require demonstrable resource at name (similar to DNS verification) |
||||
|
||||
#### 5. Transfer Fraud |
||||
|
||||
**Attack**: Forged transfer without owner's consent. |
||||
|
||||
**Mitigation**: |
||||
- Transfer requires cryptographic signature from previous owner (`prev_sig`) |
||||
- Signature includes timestamp to prevent replay attacks |
||||
- Invalid signatures cause immediate rejection by honest relays |
||||
|
||||
### Privacy Considerations |
||||
|
||||
- Registration proposals are public (necessary for consensus) |
||||
- Ownership history is permanently visible on relays |
||||
- Trust relationships are public (required for verifiable consensus) |
||||
- Clients leaking name queries to relays (similar to DNS privacy issues) |
||||
- Mitigation: Use private relays or Tor for sensitive queries |
||||
|
||||
## Discussion |
||||
|
||||
### Trust Graph Bootstrapping |
||||
|
||||
The effectiveness of trust-weighted consensus depends on establishing a healthy trust graph. This presents a chicken-and-egg problem: new relays need trust to participate, but must participate to earn trust. |
||||
|
||||
**Proposed bootstrapping strategies:** |
||||
|
||||
#### 1. Seed Relay Trust Inheritance |
||||
|
||||
New relays can inherit trust graphs from established "seed" relays: |
||||
|
||||
```json |
||||
{ |
||||
"kind": 30101, |
||||
"tags": [ |
||||
["inherit", "<seed_relay_pubkey>", "0.8"], |
||||
["inherit", "<seed_relay_pubkey2>", "0.6"] |
||||
] |
||||
} |
||||
``` |
||||
|
||||
The new relay computes its initial trust graph as a weighted average of the seed relays' graphs. Over time (30-90 days), the relay adjusts trust based on observed behavior. |
||||
|
||||
**Trade-offs:** |
||||
- ✅ Enables immediate participation |
||||
- ✅ Leverages existing reputation |
||||
- ❌ Creates trust concentrations around seed relays |
||||
- ❌ Seed relay compromise affects many descendants |
||||
|
||||
#### 2. Web of Trust Endorsements |
||||
|
||||
Established relay operators can endorse new relays through signed endorsements: |
||||
|
||||
```json |
||||
{ |
||||
"kind": 1100, |
||||
"pubkey": "<established_relay_pubkey>", |
||||
"tags": [ |
||||
["p", "<new_relay_pubkey>"], |
||||
["endorsement", "verified"], |
||||
["duration", "30d"], |
||||
["initial_trust", "0.3"] |
||||
] |
||||
} |
||||
``` |
||||
|
||||
Other relays consuming this endorsement automatically grant the new relay temporary trust (e.g., 0.3 for 30 days), after which it decays to 0 unless organically reinforced. |
||||
|
||||
**Trade-offs:** |
||||
- ✅ Decentralized trust building |
||||
- ✅ Time-limited risk exposure |
||||
- ✅ Organic network growth |
||||
- ❌ Slower bootstrap for new relays |
||||
- ❌ Endorsement spam risk |
||||
|
||||
#### 3. Proof-of-Stake Bootstrap |
||||
|
||||
New relays can stake economic value (e.g., lock tokens in a Bitcoin Lightning channel) to gain initial trust: |
||||
|
||||
```json |
||||
{ |
||||
"kind": 30103, |
||||
"pubkey": "<new_relay_pubkey>", |
||||
"tags": [ |
||||
["stake", "<lightning_invoice>", "1000000"], // 1M sats |
||||
["stake_duration", "180d"], |
||||
["stake_proof", "<proof_of_lock>"] |
||||
] |
||||
} |
||||
``` |
||||
|
||||
Relays treat staked relays as having trust proportional to stake amount. Dishonest behavior results in slashing (stake destruction). |
||||
|
||||
**Trade-offs:** |
||||
- ✅ Sybil resistant (economic cost) |
||||
- ✅ Clear incentive alignment |
||||
- ✅ Immediate trust proportional to stake |
||||
- ❌ Requires economic layer integration |
||||
- ❌ Excludes low-resource operators |
||||
- ❌ Introduces plutocracy risk |
||||
|
||||
#### 4. Proof-of-Work Bootstrap |
||||
|
||||
New relays solve computational puzzles to earn temporary trust: |
||||
|
||||
```json |
||||
{ |
||||
"kind": 30104, |
||||
"pubkey": "<new_relay_pubkey>", |
||||
"tags": [ |
||||
["pow", "<nonce>", "24"], // 24-bit difficulty |
||||
["pow_created", "<timestamp>"] |
||||
] |
||||
} |
||||
``` |
||||
|
||||
Higher difficulty equals higher initial trust. PoW expires after 90 days unless organic trust replaces it. |
||||
|
||||
**Trade-offs:** |
||||
- ✅ Sybil resistant (computational cost) |
||||
- ✅ No economic barrier |
||||
- ✅ Proven model (Hashcash, Bitcoin) |
||||
- ❌ Energy intensive |
||||
- ❌ Favors well-resourced operators |
||||
- ❌ Difficulty calibration challenges |
||||
|
||||
**Recommendation**: Implement **hybrid approach**: |
||||
- Start with seed relay inheritance (#1) for immediate participation |
||||
- Layer WoT endorsements (#2) for organic trust growth |
||||
- Optional PoW (#4) for permissionless entry without social connections |
||||
- Reserve PoS (#3) for future upgrade if economic attacks become problematic |
||||
|
||||
### Economic Incentives |
||||
|
||||
Why would relay operators honestly attest to name registrations? Without proper incentives, rational operators might: |
||||
- Refuse to attest (freeloading) |
||||
- Attest dishonestly (e.g., favoring paying customers) |
||||
- Collude to manipulate consensus |
||||
|
||||
**Proposed incentive mechanisms:** |
||||
|
||||
#### 1. Registration Fees |
||||
|
||||
Name registration proposals include optional tips to relays that attest: |
||||
|
||||
```json |
||||
{ |
||||
"kind": 30100, |
||||
"tags": [ |
||||
["d", "foo.n"], |
||||
["fee", "<lightning_invoice>", "10000"], // 10k sats |
||||
["fee_distribution", "attestors"] // or "weighted" or "threshold" |
||||
] |
||||
} |
||||
``` |
||||
|
||||
**Distribution strategies:** |
||||
- **Attestors**: Split among all relays that attested (encourages broad participation) |
||||
- **Weighted**: Proportional to trust weight (rewards influential relays) |
||||
- **Threshold**: All-or-nothing (only if consensus reached) |
||||
|
||||
**Trade-offs:** |
||||
- ✅ Direct incentive for honest attestation |
||||
- ✅ Market-driven fee discovery |
||||
- ✅ Revenue for relay operators |
||||
- ❌ Plutocracy risk (wealthy users get priority) |
||||
- ❌ Creates spam incentive (register garbage for fees) |
||||
|
||||
#### 2. Reputation Markets |
||||
|
||||
Relay operators earn reputation scores based on attestation quality: |
||||
|
||||
```json |
||||
{ |
||||
"kind": 30105, |
||||
"tags": [ |
||||
["p", "<relay_pubkey>"], |
||||
["metric", "accuracy", "0.97"], // % of attestations matching consensus |
||||
["metric", "uptime", "0.99"], // % of proposals attested |
||||
["metric", "latency", "15"], // avg attestation time (seconds) |
||||
["period", "<start>", "<end>"] |
||||
] |
||||
} |
||||
``` |
||||
|
||||
High-reputation relays gain: |
||||
- Greater trust from other relays |
||||
- Priority in client queries |
||||
- Higher economic value (e.g., relay subscription fees) |
||||
|
||||
**Trade-offs:** |
||||
- ✅ Long-term incentive alignment |
||||
- ✅ Punishes dishonest behavior |
||||
- ✅ No direct economic barrier |
||||
- ❌ Reputation calculation is complex |
||||
- ❌ Slow feedback loop (months to build reputation) |
||||
|
||||
#### 3. Mutual Benefit Networks |
||||
|
||||
Relays form explicit cooperation agreements: |
||||
|
||||
```json |
||||
{ |
||||
"kind": 30106, |
||||
"tags": [ |
||||
["p", "<partner_relay1>", "wss://relay1.com"], |
||||
["p", "<partner_relay2>", "wss://relay2.com"], |
||||
["agreement", "reciprocal_attestation"], |
||||
["sla", "95_percent_uptime"] |
||||
] |
||||
} |
||||
``` |
||||
|
||||
Relays attest to partners' proposals in exchange for reciprocal service. Violating agreements results in removal from the network. |
||||
|
||||
**Trade-offs:** |
||||
- ✅ No economic transactions needed |
||||
- ✅ Builds social cohesion |
||||
- ✅ Flexible SLA definitions |
||||
- ❌ Creates relay cartels |
||||
- ❌ Excludes new entrants |
||||
- ❌ May lead to consensus fragmentation |
||||
|
||||
#### 4. Altruism + Low Operational Cost |
||||
|
||||
If attestation cost is sufficiently low, relay operators may participate altruistically: |
||||
|
||||
- Storage: ~100 MB for 7-day attestation window |
||||
- Bandwidth: ~1 KB per attestation |
||||
- Computation: Simple signature verification and weighted sum |
||||
|
||||
At 1000 registrations/day with 10% sparse attestation: |
||||
- **Cost per relay**: <$1/month in infrastructure |
||||
- **Comparable to**: Running a Bitcoin full node or Nostr relay |
||||
|
||||
Many operators already run relays without direct compensation, motivated by: |
||||
- Ideological alignment (decentralization, censorship resistance) |
||||
- Supporting applications they use |
||||
- Technical interest and experimentation |
||||
|
||||
**Trade-offs:** |
||||
- ✅ No economic complexity |
||||
- ✅ Aligns with Nostr's ethos |
||||
- ✅ Proven model (Nostr relay operators, Bitcoin nodes) |
||||
- ❌ May not scale to high-value registrations |
||||
- ❌ Vulnerable to tragedy of the commons |
||||
|
||||
**Recommendation**: Start with **altruism (#4)** supplemented by **reputation (#2)**: |
||||
- Most relay operators will participate without direct payment (proven by existing Nostr network) |
||||
- Implement transparent reputation metrics to reward high-quality attestors |
||||
- Enable optional tipping (#1) for users who want priority or to support relays |
||||
- Reserve mutual benefit networks (#3) for enterprise deployments |
||||
|
||||
### Client Conflict Resolution |
||||
|
||||
Clients querying name ownership may receive conflicting responses from different relays due to: |
||||
1. Trust graph divergence (different relays trust different attestors) |
||||
2. Network partitions (some relays missed attestations) |
||||
3. Byzantine relays (malicious responses) |
||||
|
||||
**Conflict resolution strategies:** |
||||
|
||||
#### 1. Majority Consensus |
||||
|
||||
Client queries N relays (recommended N=5) and accepts the majority response: |
||||
|
||||
```python |
||||
def resolve_name(name, relays): |
||||
responses = [] |
||||
for relay in relays: |
||||
owner = query_relay(relay, name) |
||||
responses.append(owner) |
||||
|
||||
# Count occurrences |
||||
counts = {} |
||||
for owner in responses: |
||||
counts[owner] = counts.get(owner, 0) + 1 |
||||
|
||||
# Return majority (>50%) |
||||
for owner, count in counts.items(): |
||||
if count > len(relays) / 2: |
||||
return owner |
||||
|
||||
return None # No majority |
||||
``` |
||||
|
||||
**Trade-offs:** |
||||
- ✅ Simple and intuitive |
||||
- ✅ Byzantine fault tolerant (up to N/2 malicious relays) |
||||
- ✅ Works with any relay set |
||||
- ❌ Requires querying multiple relays (latency) |
||||
- ❌ No nuance for different relay qualities |
||||
|
||||
#### 2. Trust-Weighted Voting |
||||
|
||||
Client maintains its own trust graph and weights relay responses: |
||||
|
||||
```python |
||||
def resolve_name_weighted(name, relays, trust_scores): |
||||
responses = {} |
||||
for relay in relays: |
||||
owner = query_relay(relay, name) |
||||
weight = trust_scores.get(relay, 0.5) # default 0.5 |
||||
responses[owner] = responses.get(owner, 0) + weight |
||||
|
||||
# Return highest weighted response |
||||
return max(responses, key=responses.get) |
||||
``` |
||||
|
||||
**Trade-offs:** |
||||
- ✅ Rewards high-quality relays |
||||
- ✅ Resilient to Sybil attacks |
||||
- ✅ Customizable trust model |
||||
- ❌ Client must maintain trust graph |
||||
- ❌ More complex UX |
||||
|
||||
#### 3. Consensus Confidence Scoring |
||||
|
||||
Relays include confidence scores in name state events (kind 30102): |
||||
|
||||
```json |
||||
{ |
||||
"kind": 30102, |
||||
"tags": [ |
||||
["d", "foo.n"], |
||||
["owner", "<pubkey>"], |
||||
["confidence", "0.87"], // 87% of trust weight agreed |
||||
["attestations", "42"] // 42 relays attested |
||||
] |
||||
} |
||||
``` |
||||
|
||||
Client queries multiple relays and uses the response with highest confidence: |
||||
|
||||
```python |
||||
def resolve_name_confidence(name, relays): |
||||
best_response = None |
||||
best_confidence = 0 |
||||
|
||||
for relay in relays: |
||||
state = query_relay(relay, name) # returns kind 30102 |
||||
confidence = float(state.tags["confidence"]) |
||||
|
||||
if confidence > best_confidence: |
||||
best_confidence = confidence |
||||
best_response = state.tags["owner"] |
||||
|
||||
# Warn if low confidence |
||||
if best_confidence < 0.6: |
||||
warn_user("Low consensus confidence") |
||||
|
||||
return best_response |
||||
``` |
||||
|
||||
**Trade-offs:** |
||||
- ✅ Transparent consensus strength |
||||
- ✅ User warnings for disputed names |
||||
- ✅ Minimal client complexity |
||||
- ❌ Trusts relay's confidence calculation |
||||
- ❌ Relays could lie about confidence |
||||
|
||||
#### 4. Recursive Attestation Verification |
||||
|
||||
Client fetches attestations (kind 20100) from relays and recomputes consensus locally: |
||||
|
||||
```python |
||||
def resolve_name_verify(name, relays): |
||||
# 1. Get all proposals for this name |
||||
proposals = [] |
||||
for relay in relays: |
||||
props = query_relay(relay, kind=30100, filter={"d": name}) |
||||
proposals.extend(props) |
||||
|
||||
# 2. Get all attestations for these proposals |
||||
attestations = [] |
||||
for relay in relays: |
||||
atts = query_relay(relay, kind=20100, filter={"e": [p.id for p in proposals]}) |
||||
attestations.extend(atts) |
||||
|
||||
# 3. Get trust graphs |
||||
trust_graphs = [] |
||||
for relay in relays: |
||||
graphs = query_relay(relay, kind=30101) |
||||
trust_graphs.extend(graphs) |
||||
|
||||
# 4. Recompute consensus locally |
||||
return compute_consensus(proposals, attestations, trust_graphs) |
||||
``` |
||||
|
||||
**Trade-offs:** |
||||
- ✅ Maximum trustlessness |
||||
- ✅ Client verifies everything |
||||
- ✅ Can audit relay dishonesty |
||||
- ❌ Significant bandwidth and computation |
||||
- ❌ Complex client implementation |
||||
- ❌ May timeout on large registries |
||||
|
||||
#### 5. Hybrid: Quick Query with Dispute Resolution |
||||
|
||||
Normal case: Query 3 relays, accept majority (fast path) |
||||
Dispute case: If no majority, fetch attestations and recompute (slow path) |
||||
|
||||
```python |
||||
def resolve_name_hybrid(name, relays): |
||||
# Fast path: majority consensus |
||||
responses = [query_relay(r, name) for r in relays] |
||||
majority = get_majority(responses) |
||||
|
||||
if majority: |
||||
return majority |
||||
|
||||
# Slow path: fetch attestations and verify |
||||
return resolve_name_verify(name, relays) |
||||
``` |
||||
|
||||
**Trade-offs:** |
||||
- ✅ Fast common case (1 round trip) |
||||
- ✅ Trustless dispute resolution |
||||
- ✅ Best of both worlds |
||||
- ❌ Unpredictable latency (95% fast, 5% slow) |
||||
|
||||
**Recommendation**: Implement **hybrid approach (#5)**: |
||||
- Default to majority consensus for speed |
||||
- Fall back to attestation verification on conflicts |
||||
- Display confidence scores (#3) to users |
||||
- Allow power users to enable trust-weighted voting (#2) |
||||
- Provide CLI tool for full recursive verification (#4) for auditing |
||||
|
||||
This provides good UX for most users while maintaining trustless properties for disputed or high-value names. |
||||
|
||||
## References |
||||
|
||||
- [NIP-01: Basic protocol flow](https://github.com/nostr-protocol/nips/blob/master/01.md) |
||||
- [NIP-33: Parameterized replaceable events](https://github.com/nostr-protocol/nips/blob/master/33.md) |
||||
- [NIP-65: Relay list metadata](https://github.com/nostr-protocol/nips/blob/master/65.md) |
||||
- PageRank algorithm: Brin, S. and Page, L. (1998). "The anatomy of a large-scale hypertextual Web search engine" |
||||
- Byzantine Generals Problem: Lamport, L., Shostak, R., and Pease, M. (1982) |
||||
|
||||
## Changelog |
||||
|
||||
- 2025-01-XX: Initial draft |
||||
Loading…
Reference in new issue