This commit is contained in:
Raven Scott
2025-12-17 20:05:50 -05:00
commit 742e27d3f7
276 changed files with 89838 additions and 0 deletions
+414
View File
@@ -0,0 +1,414 @@
# P2NS Consensus Mechanism
This document provides a deep dive into the P2NS consensus mechanism for domain ownership resolution.
## Overview
P2NS uses a quorum-based consensus mechanism to resolve domain ownership disputes. When multiple peers claim the same domain, the network votes to determine the legitimate owner. This enables decentralized domain management without a central authority.
**Key goals:**
- Prevent domain squatting through voting
- Handle network partitions gracefully
- Resolve ties deterministically
- Support single-node operation (local claims)
## Data Model
Domain claims and votes are stored in the Autopass distributed ledger using a key-value format.
### Claims
Claims assert ownership of a domain:
```
Key: claim:{domain}:{claimant}
Value: {"hash": "hs://...", "clients": [...], "timestamp": 1704067200000, "ssl": false}
```
| Field | Type | Description |
|-------|------|-------------|
| `hash` | string | Holesail connection hash for the domain |
| `clients` | array | Service definitions (serviceName, key, port, protocol) |
| `timestamp` | number | Unix timestamp when claim was created |
| `ssl` | boolean | Whether the Holesail connection uses SSL/TLS |
**Legacy format:** Older claims may store just the hash string without JSON wrapper.
### Votes
Votes support a specific claimant for a domain:
```
Key: vote:{domain}:{claimant}:{voter}
Value: "1"
```
| Component | Description |
|-----------|-------------|
| `domain` | The domain being voted on |
| `claimant` | Public key of the claim being supported |
| `voter` | Public key of the peer casting the vote |
## Consensus Algorithm
### Resolution Flow
```mermaid
flowchart TD
Start[Get Consensus State] --> CheckPass{dnsPass initialized?}
CheckPass -->|No| ErrorState[Return error state]
CheckPass -->|Yes| CollectClaims[Collect all claims for domain]
CollectClaims --> CheckClaims{Any claims exist?}
CheckClaims -->|No| NoClaims[Status: no_claims]
CheckClaims -->|Yes| CollectVotes[Collect and validate votes]
CollectVotes --> CalcQuorum[Calculate quorum requirement]
CalcQuorum --> CheckQuorum{Quorum met?}
CheckQuorum -->|No| CheckSingleLocal{Single local claim?}
CheckSingleLocal -->|Yes| ResolvedLocal[Status: resolved]
CheckSingleLocal -->|No| InsufficientQuorum[Status: insufficient_quorum]
CheckQuorum -->|Yes| FindWinner[Find claimant with most votes]
FindWinner --> CheckTie{Multiple winners?}
CheckTie -->|No| Resolved[Status: resolved]
CheckTie -->|Yes| TieBreaker[Apply tie-breaker strategy]
TieBreaker --> TieResolved[Status: tie - resolved via tie-breaker]
```
### Vote Collection
1. Iterate through all ledger entries
2. Filter votes matching `vote:{domain}:*`
3. Parse vote key to extract claimant and voter
4. Validate each vote (must reference existing claim)
5. Count valid votes per claimant
### Quorum Calculation
The minimum votes required for consensus:
```javascript
minVotes = max(CONSENSUS_MIN_VOTES, ceil(activePeers * CONSENSUS_QUORUM_THRESHOLD))
```
Where:
- `activePeers` = connected peers + 1 (local node)
- `CONSENSUS_QUORUM_THRESHOLD` = 0.5 (default)
- `CONSENSUS_MIN_VOTES` = 2 (default)
**Example:** With 5 active peers and default settings:
- `ceil(5 * 0.5) = 3`
- `max(2, 3) = 3` votes required
### Resolution States
| Status | Description |
|--------|-------------|
| `resolved` | Single winner determined by vote count or tie-breaker |
| `insufficient_quorum` | Not enough votes to reach consensus |
| `tie` | Multiple claimants tied; resolved via tie-breaker |
| `no_claims` | No claims exist for this domain |
| `error` | System error (e.g., dnsPass not initialized) |
### Special Case: Single Local Claim
When there are no votes but only the local peer has claimed a domain, it's automatically resolved to the local claimant. This enables single-node operation without requiring external votes.
## Tie-Breaking Strategies
When multiple claimants have equal votes, a tie-breaker determines the winner.
### timestamp (Default)
First-come-first-served: the oldest claim wins.
```javascript
candidates.sort((a, b) => claimTimestamps[a] - claimTimestamps[b])[0]
```
**Rationale:** Rewards early adopters and prevents hostile takeovers of established domains.
### claimant_age
Prefers the local writer if they're a candidate, otherwise falls back to lexicographic ordering.
```javascript
if (candidates.includes(localWriter)) return localWriter;
return candidates.sort((a, b) => a.localeCompare(b))[0];
```
**Rationale:** Gives local claims priority while maintaining determinism.
### lexicographic
Prefers local writer, then sorts candidates alphabetically by public key.
```javascript
if (candidates.includes(localWriter)) return localWriter;
return candidates.sort((a, b) => a.localeCompare(b))[0];
```
**Rationale:** Ensures all nodes reach the same conclusion deterministically.
## Auto-Voting
P2NS automatically casts votes under certain conditions.
### When Auto-Votes Occur
1. **On startup:** After connecting to the network
2. **On peer connection:** When new peers join
3. **On claim discovery:** When new claims are replicated
4. **On manual trigger:** Via `/api/consensus/recalculate`
### Auto-Vote Logic
```javascript
async function autoVoteForDomain(domain, entries) {
// Get claims for this domain
const claims = getClaimsForDomain(domain, entries);
// Check if local peer already voted
if (hasLocalVote(domain, entries)) return;
// Vote for local claim if exists
if (claims.has(localWriter)) {
await castVote(domain, localWriter);
return;
}
// Vote for claim with most existing votes
const winner = getLeadingClaimant(domain, entries);
if (winner) {
await castVote(domain, winner);
}
}
```
### Vote Validation
Votes must reference an existing claim to be counted:
```javascript
function validateVote(claimant, claims) {
if (!CONSENSUS_VOTE_VALIDATION) return true;
if (!claims.hasOwnProperty(claimant)) {
consensusMetrics.validationFailures++;
return false;
}
return true;
}
```
Invalid votes are logged and excluded from vote counts.
## Configuration
Configure consensus behavior via environment variables:
| Variable | Default | Description |
|----------|---------|-------------|
| `CONSENSUS_QUORUM_THRESHOLD` | `0.5` | Percentage of peers required (0.0-1.0) |
| `CONSENSUS_MIN_VOTES` | `2` | Minimum votes regardless of peer count |
| `CONSENSUS_TIE_BREAKER` | `timestamp` | Strategy: `timestamp`, `claimant_age`, `lexicographic` |
| `CONSENSUS_VOTE_VALIDATION` | `true` | Validate votes reference existing claims |
| `CONSENSUS_IMMEDIATE_UPDATE` | `true` | Update resolution immediately on new votes |
### Tuning Recommendations
**High-security deployments:**
```env
CONSENSUS_QUORUM_THRESHOLD=0.67
CONSENSUS_MIN_VOTES=3
CONSENSUS_VOTE_VALIDATION=true
```
**Small networks (2-3 peers):**
```env
CONSENSUS_QUORUM_THRESHOLD=0.5
CONSENSUS_MIN_VOTES=1
```
**Single-node operation:**
```env
CONSENSUS_MIN_VOTES=0
```
## Caching
Consensus uses two-level caching for performance.
### Entries Cache
All ledger entries are cached to avoid repeated expensive lookups:
- **TTL:** 5 seconds
- **Scope:** All entries in Autopass
- **Invalidation:** On write operations, manual invalidation
### Consensus State Cache
Per-domain consensus results are cached:
- **TTL:** 10 seconds
- **Scope:** Individual domain consensus state
- **Invalidation:** On cache expiry, manual recalculation
### Cache Invalidation
```javascript
function invalidateEntriesCache() {
entriesCache = null;
entriesCacheTimestamp = 0;
consensusStateCache.clear();
}
```
Called after:
- Adding/removing claims
- Casting votes
- Manual recalculation via API
## Metrics
Consensus operations are tracked for monitoring:
| Metric | Description |
|--------|-------------|
| `resolutions` | Total successful domain resolutions |
| `quorumFailures` | Times quorum was not met |
| `ties` | Ties requiring tie-breaker |
| `validationFailures` | Invalid votes rejected |
| `totalVotes` | Total votes cast across all domains |
| `avgVotesPerDomain` | Average votes per domain |
| `domainResolutions` | Per-domain resolution/failure counts |
### Accessing Metrics
**Via API:**
```bash
curl https://p2ns.admin/api/consensus/metrics
```
**Via SDK:**
```javascript
const metrics = sdk.dns.getConsensusMetrics();
```
## API Endpoints
### Get Consensus State
```bash
GET /api/consensus/{domain}
```
Returns current consensus state for a domain including vote counts, quorum status, and resolved claimant.
### Get Metrics
```bash
GET /api/consensus/metrics
```
Returns aggregate consensus metrics across all domains.
### Force Recalculation
```bash
POST /api/consensus/recalculate
POST /api/consensus/recalculate/{domain}
```
Invalidates cache and triggers fresh consensus calculation. Useful after network changes or debugging.
## Example Scenarios
### Scenario 1: Single Owner
```
Peers: A (local)
Claims: A claims example.tld
Votes: (none)
Result: resolved -> A
Reason: Single local claim, no quorum needed
```
### Scenario 2: Clear Winner
```
Peers: A, B, C (3 total)
Claims: A claims example.tld, B claims example.tld
Votes: A votes for A, B votes for A, C votes for A
Quorum: max(2, ceil(3 * 0.5)) = 2
Total votes for A: 3
Result: resolved -> A
Reason: A has majority votes, quorum met
```
### Scenario 3: Tie with Timestamp Resolution
```
Peers: A, B, C, D (4 total)
Claims:
- A claims example.tld at T=1000
- B claims example.tld at T=2000
Votes: A->A, B->B, C->A, D->B
Vote counts: A=2, B=2 (tie)
Tie-breaker: timestamp
Winner: A (older claim)
Result: tie -> A
```
### Scenario 4: Insufficient Quorum
```
Peers: A, B, C, D, E (5 total)
Claims: A claims example.tld
Votes: A votes for A
Quorum: max(2, ceil(5 * 0.5)) = 3
Total votes: 1
Result: insufficient_quorum
Reason: Only 1 vote, need 3
```
## Troubleshooting
### Domain Not Resolving
1. Check if claim exists: `GET /api/entries`
2. Check consensus state: `GET /api/consensus/{domain}`
3. Verify quorum: Are enough peers connected?
4. Force recalculation: `POST /api/consensus/recalculate/{domain}`
### Unexpected Winner
1. Check vote counts in consensus state
2. Verify tie-breaker strategy matches expectations
3. Check claim timestamps if using `timestamp` strategy
4. Review vote validation failures in metrics
### High Quorum Failures
1. Check peer count: `GET /api/peers`
2. Lower `CONSENSUS_MIN_VOTES` for small networks
3. Adjust `CONSENSUS_QUORUM_THRESHOLD` if needed
## Related Documentation
- [GLOSSARY.md](GLOSSARY.md) - Terms and concepts
- [ARCHITECTURE.md](ARCHITECTURE.md) - System architecture
- [RESTAPI.md](RESTAPI.md) - API documentation
- [README.md](../README.md) - Main documentation