forked from snxraven/p2ns
Fix Atomic state
This commit is contained in:
+13
-11
@@ -4,6 +4,7 @@ const { logDebug, logError, logInfo, logWarn } = require('../infrastructure/logg
|
||||
const { trackDomainEvent, trackConsensusEvent } = require('../maintenance/metrics');
|
||||
const { validateConfig } = require('../infrastructure/config');
|
||||
const { secondsToMs, getPersistentPublicKey } = require('../infrastructure/utils');
|
||||
const { dnsPassAdd, dnsPassRemove, dnsPassGet, whenDnsPassIdle } = require('./dns-pass-queue');
|
||||
|
||||
// Get consensus configuration
|
||||
let consensusConfig = null;
|
||||
@@ -94,6 +95,7 @@ async function getAllEntries(pass = state.dnsPass, useCache = true) {
|
||||
|
||||
const entries = [];
|
||||
try {
|
||||
await whenDnsPassIdle();
|
||||
const stream = pass.list();
|
||||
for await (const entry of stream) {
|
||||
entries.push({
|
||||
@@ -107,8 +109,8 @@ async function getAllEntries(pass = state.dnsPass, useCache = true) {
|
||||
logDebug('Core', `Cached ${entries.length} entries`);
|
||||
} catch (err) {
|
||||
const msg = err.message || '';
|
||||
if (msg.includes('SESSION_CLOSED') || msg.includes('closing core')) {
|
||||
logWarn('Core', `dnsPass core is closing — stop the node and restart with --clean if this persists after upgrading Autopass: ${msg}`);
|
||||
if (msg.includes('SESSION_CLOSED') || msg.includes('closing core') || msg.includes('Atomic state must flush')) {
|
||||
logWarn('Core', `dnsPass core busy or closing — restart with --clean if this persists: ${msg}`);
|
||||
} else {
|
||||
logError('Core', `Error getting entries: ${msg}`);
|
||||
}
|
||||
@@ -216,7 +218,7 @@ async function updateClaimClients(domain, claimant, clients) {
|
||||
|
||||
// SAFE: Remove old claim and add updated one (ownership verified)
|
||||
await safeRemoveClaim(claimKey, claimant);
|
||||
await pass.add(claimKey, updatedValue);
|
||||
await dnsPassAdd(pass, claimKey, updatedValue);
|
||||
|
||||
invalidateEntriesCache();
|
||||
logInfo('Core', `Updated claim clients for ${domain} by ${claimant}`);
|
||||
@@ -674,7 +676,7 @@ async function voteForDomain(domain, claimant, allEntries = null) {
|
||||
// Remove existing votes for other claimants
|
||||
for (const [votedClaimant, voteKey] of myVotes) {
|
||||
if (votedClaimant !== claimant) {
|
||||
await pass.remove(voteKey);
|
||||
await dnsPassRemove(pass, voteKey);
|
||||
logInfo('Core', `Removed vote for ${domain} claimant ${votedClaimant}`);
|
||||
}
|
||||
}
|
||||
@@ -682,7 +684,7 @@ async function voteForDomain(domain, claimant, allEntries = null) {
|
||||
// Add new vote if not already voting for this claimant
|
||||
if (!myVotes.has(claimant)) {
|
||||
const voteKey = `vote:${domain}:${claimant}:${localWriter}`;
|
||||
await pass.add(voteKey, claims[claimant]);
|
||||
await dnsPassAdd(pass, voteKey, claims[claimant]);
|
||||
logInfo('Core', `Voted for ${domain} claimant ${claimant}`);
|
||||
invalidateEntriesCache();
|
||||
}
|
||||
@@ -735,7 +737,7 @@ async function removeDomain(domain) {
|
||||
for (const entry of allEntries) {
|
||||
if (entry.key.startsWith(`claim:${domain}:`) ||
|
||||
entry.key.startsWith(`vote:${domain}:`)) {
|
||||
await pass.remove(entry.key);
|
||||
await dnsPassRemove(pass, entry.key);
|
||||
logDebug('Core', `Removed ${entry.key} for domain removal`);
|
||||
}
|
||||
}
|
||||
@@ -769,7 +771,7 @@ async function removeOwnClaimAndVotes(domain, localWriter) {
|
||||
// Remove ONLY the user's own claim record
|
||||
// Format: claim:domain:claimant
|
||||
if (entry.key === `claim:${domain}:${localWriter}`) {
|
||||
await pass.remove(entry.key);
|
||||
await dnsPassRemove(pass, entry.key);
|
||||
logInfo('Core', `Removed own claim: ${entry.key}`);
|
||||
removedClaims++;
|
||||
}
|
||||
@@ -780,7 +782,7 @@ async function removeOwnClaimAndVotes(domain, localWriter) {
|
||||
const parts = entry.key.split(':');
|
||||
if (parts.length === 4 && parts[3] === localWriter) {
|
||||
// This is a vote cast BY the user (voter is localWriter)
|
||||
await pass.remove(entry.key);
|
||||
await dnsPassRemove(pass, entry.key);
|
||||
logInfo('Core', `Removed vote cast by user: ${entry.key}`);
|
||||
removedVotes++;
|
||||
}
|
||||
@@ -825,7 +827,7 @@ async function safeRemoveClaim(claimKey, expectedClaimant) {
|
||||
}
|
||||
|
||||
// Safe to remove - ownership verified
|
||||
await state.dnsPass.remove(claimKey);
|
||||
await dnsPassRemove(state.dnsPass, claimKey);
|
||||
logInfo('Core', `Safely removed claim ${claimKey} (owned by ${claimant})`);
|
||||
return true;
|
||||
}
|
||||
@@ -847,7 +849,7 @@ async function removeAllRecords() {
|
||||
for (const entry of allEntries) {
|
||||
if (entry.key.startsWith('claim:') || entry.key.startsWith('vote:')) {
|
||||
try {
|
||||
await pass.remove(entry.key);
|
||||
await dnsPassRemove(pass, entry.key);
|
||||
removed++;
|
||||
logDebug('Core', `Removed ${entry.key}`);
|
||||
} catch (err) {
|
||||
@@ -899,7 +901,7 @@ async function cleanupRedundantClientEntries() {
|
||||
for (const entry of allEntries) {
|
||||
if (entry.key.startsWith('clients:')) {
|
||||
try {
|
||||
await pass.remove(entry.key);
|
||||
await dnsPassRemove(pass, entry.key);
|
||||
removed++;
|
||||
logDebug('Core', `Removed redundant client entry: ${entry.key}`);
|
||||
} catch (err) {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Serializes Autopass / Autobase operations on the master corestore.
|
||||
* Concurrent createInvite, list(), add(), and remove() calls cause
|
||||
* "Atomic state must flush to parent" on Hypercore 11.
|
||||
*/
|
||||
|
||||
let chain = Promise.resolve();
|
||||
|
||||
function enqueueDnsPass(operation) {
|
||||
const run = chain.then(() => operation());
|
||||
chain = run.then(
|
||||
() => {},
|
||||
() => {}
|
||||
);
|
||||
return run;
|
||||
}
|
||||
|
||||
/** Wait until queued dnsPass work finishes (for reads). */
|
||||
function whenDnsPassIdle() {
|
||||
return chain;
|
||||
}
|
||||
|
||||
async function createInvite(pass, opts) {
|
||||
return enqueueDnsPass(() => pass.createInvite(opts));
|
||||
}
|
||||
|
||||
async function dnsPassAdd(pass, key, value, file) {
|
||||
return enqueueDnsPass(() => pass.add(key, value, file));
|
||||
}
|
||||
|
||||
async function dnsPassRemove(pass, key) {
|
||||
return enqueueDnsPass(() => pass.remove(key));
|
||||
}
|
||||
|
||||
async function dnsPassGet(pass, key) {
|
||||
await whenDnsPassIdle();
|
||||
return pass.get(key);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
enqueueDnsPass,
|
||||
whenDnsPassIdle,
|
||||
createInvite,
|
||||
dnsPassAdd,
|
||||
dnsPassRemove,
|
||||
dnsPassGet
|
||||
};
|
||||
@@ -3,6 +3,7 @@ const { logDebug, logInfo, logError } = require('../infrastructure/logger');
|
||||
const { getAllEntries, autoVoteForDomain, invalidateEntriesCache, parseClaimValue, safeRemoveClaim } = require('./core');
|
||||
const { trackDomainEvent } = require('../maintenance/metrics');
|
||||
const { getPersistentPublicKey } = require('../infrastructure/utils');
|
||||
const { dnsPassAdd, dnsPassGet } = require('./dns-pass-queue');
|
||||
// Initialize reserved IPs set if not already present
|
||||
if (!state.reservedIPs) {
|
||||
state.reservedIPs = new Set(['127.0.0.1']);
|
||||
@@ -65,15 +66,15 @@ async function addDomain(domain, hash, ssl = false) {
|
||||
// SAFE: Remove old claim and add updated one (ownership verified above)
|
||||
// Use the safe remove helper for consistent validation
|
||||
await safeRemoveClaim(claimKey, claimant);
|
||||
await pass.add(claimKey, claimValue);
|
||||
await dnsPassAdd(pass, claimKey, claimValue);
|
||||
|
||||
logInfo('Domains', `Updated claim ${claimKey} with new hash ${hash} and SSL=${sslValue}`);
|
||||
} else {
|
||||
// New claim - add it
|
||||
logDebug('Domains', `Adding new domain ${domain} with hash ${hash} and SSL=${sslValue} as claim ${claimKey} at timestamp ${timestamp}`);
|
||||
await pass.add(claimKey, claimValue);
|
||||
await dnsPassAdd(pass, claimKey, claimValue);
|
||||
|
||||
const verified = await pass.get(claimKey);
|
||||
const verified = await dnsPassGet(pass, claimKey);
|
||||
logDebug('Domains', `Verified add for ${claimKey}: ${verified ? verified.toString('utf8') : 'null'}`);
|
||||
logInfo('Domains', `Domain ${domain} added as claim ${claimKey} with timestamp ${timestamp}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user