feat: Add P2P Domain Conflicts management

Implement comprehensive P2P domain conflict resolution allowing users to choose between local claim hashes and consensus-resolved hashes for domains where they have local claims but another claimant won consensus.

Key features:
- P2P Domain Conflicts UI tab in Local DNS section
- Hash preference toggle (local vs resolved) with automatic client restart
- Extended selector_cache.json to store hashPreferences alongside versionPreferences
- DNS cache invalidation for immediate preference application
- REST API endpoints for conflict detection and preference management
- Automatic Holesail client restart when hash preferences change
- Complete documentation updates across README, API docs, and glossary

Resolves conflicts between local claims and consensus resolution by giving users control over which hash their domain resolves to, with seamless client management ensuring immediate effect.
This commit is contained in:
Raven Scott
2025-12-26 16:36:19 -05:00
parent 480f99ae28
commit 01acfb766c
22 changed files with 1193 additions and 65 deletions
+59 -2
View File
@@ -446,13 +446,69 @@ async function getConsensusState(domain) {
}
async function getHashForDomain(domain) {
const localWriter = getPersistentPublicKey();
const hashPreference = state.hashPreferences.get(domain);
// Check if user has a hash preference set (from P2P Domain Conflicts configuration)
if (hashPreference === 'local' && localWriter) {
// User prefers to use their local claim hash
const localHash = await getLocalClaimHash(domain, localWriter);
if (localHash) {
logInfo('Core', `Using local claim hash for ${domain} due to hash preference: ${localHash}`);
return localHash;
}
// Fall back to resolved hash if local claim not found
logWarn('Core', `Hash preference set to 'local' for ${domain} but local claim not found, falling back to resolved hash`);
} else if (hashPreference === 'resolved') {
logInfo('Core', `Using resolved consensus hash for ${domain} due to hash preference`);
}
// Use resolved hash (default behavior or when preference is 'resolved')
const consensusState = await getConsensusState(domain);
if (consensusState.status === 'resolved') {
if (!hashPreference) {
logDebug('Core', `Using resolved consensus hash for ${domain} (no preference set): ${consensusState.hash}`);
}
return consensusState.hash;
}
// For backward compatibility, if no preference set and not resolved,
// but user has a local claim, use local claim hash (default to 'local')
if (!hashPreference && localWriter) {
const localHash = await getLocalClaimHash(domain, localWriter);
if (localHash) {
logInfo('Core', `Using local claim hash for ${domain} (fallback, no preference set): ${localHash}`);
return localHash;
}
}
// For backward compatibility, return null if not resolved
logDebug('Core', `No hash available for ${domain} (not resolved, no local claim, or no preference)`);
return null;
}
async function getLocalClaimHash(domain, localWriter) {
const pass = state.dnsPass;
if (!pass || !localWriter) {
return null;
}
try {
await pass.ready();
const allEntries = await getAllEntries(pass, true);
const claimKey = `claim:${domain}:${localWriter}`;
for (const entry of allEntries) {
if (entry.key === claimKey) {
const parsed = parseClaimValue(entry.value);
return parsed.hash;
}
}
} catch (err) {
logError('Core', `Error getting local claim hash for domain ${domain}: ${err.message}`);
}
return null;
}
@@ -764,6 +820,7 @@ async function cleanupRedundantClientEntries() {
module.exports = {
getHashForDomain,
getDomainSSLStatus,
getLocalClaimHash,
doAutoVotes,
getAllEntries,
autoVoteForDomain,