Cut over domain resolution to an Autobase apply-based sidecar fed by claim/vote dual-writes from dnsPass. Remove the legacy full-KV scan getConsensusState path and wire all reads through consensus-view. - Add consensus-resolver, consensus-events, consensus-apply, consensus-autobase, and consensus-view modules - Dual-append consensus events on claim/vote dnsPassAdd/Remove - Bootstrap sidecar from existing KV entries; persist consensusAutobaseKey in network manifest - Expose sidecar health via GET /api/consensus/status and metrics - Add consensus-resolver and consensus-apply unit tests - Expand RFC 0001 to Implemented; update CONSENSUS.md Rollback: deploy prior release; KV data unchanged, sidecar rebuilds on next startup.
134 lines
4.0 KiB
JavaScript
134 lines
4.0 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Unit tests for consensus apply + bootstrap event ordering.
|
|
*/
|
|
|
|
const assert = require('assert');
|
|
const { createEmptyViewState, applyEventToView } = require('../includes/core/consensus-apply');
|
|
const { EVENT_TYPES, entriesToBootstrapEvents } = require('../includes/core/consensus-events');
|
|
const {
|
|
resolveDomainConsensus,
|
|
buildDomainSnapshotFromView
|
|
} = require('../includes/core/consensus-resolver');
|
|
|
|
const CONFIG = {
|
|
CONSENSUS_QUORUM_THRESHOLD: 0.5,
|
|
CONSENSUS_MIN_VOTES: 2,
|
|
CONSENSUS_TIE_BREAKER: 'timestamp',
|
|
CONSENSUS_VOTE_VALIDATION: true
|
|
};
|
|
|
|
function replayEvents(events) {
|
|
const view = createEmptyViewState();
|
|
for (const event of events) {
|
|
applyEventToView(view, event);
|
|
}
|
|
return view;
|
|
}
|
|
|
|
function resolveFromView(view, domain, localWriter = 'local', activePeers = 4) {
|
|
const snapshot = buildDomainSnapshotFromView(view, domain);
|
|
return resolveDomainConsensus({
|
|
domain,
|
|
claims: snapshot.claims,
|
|
voterVotes: snapshot.voterVotes,
|
|
config: CONFIG,
|
|
localWriter,
|
|
activePeers
|
|
});
|
|
}
|
|
|
|
function testBootstrapOrdering() {
|
|
const entries = [
|
|
{ key: 'vote:demo.test:peer-b:voter-1', value: '1' },
|
|
{ key: 'claim:demo.test:peer-b', value: JSON.stringify({ hash: 'hs://b', timestamp: 2000 }) },
|
|
{ key: 'claim:demo.test:peer-a', value: JSON.stringify({ hash: 'hs://a', timestamp: 1000 }) },
|
|
{ key: 'vote:demo.test:peer-a:voter-1', value: '1' },
|
|
{ key: 'vote:demo.test:peer-a:voter-2', value: '1' }
|
|
];
|
|
const events = entriesToBootstrapEvents(entries);
|
|
assert.strictEqual(events[0].type, EVENT_TYPES.CLAIM_UPSERT);
|
|
assert.strictEqual(events[0].claimant, 'peer-a');
|
|
assert.strictEqual(events[1].claimant, 'peer-b');
|
|
assert.ok(events[2].type.startsWith('vote_'));
|
|
}
|
|
|
|
function testClaimRemoveDropsVotes() {
|
|
const view = replayEvents([
|
|
{
|
|
type: EVENT_TYPES.CLAIM_UPSERT,
|
|
domain: 'x.test',
|
|
claimant: 'a',
|
|
hash: 'hs://a',
|
|
timestamp: 1,
|
|
ssl: false,
|
|
clients: []
|
|
},
|
|
{ type: EVENT_TYPES.VOTE_UPSERT, domain: 'x.test', claimant: 'a', voter: 'v1' },
|
|
{ type: EVENT_TYPES.CLAIM_REMOVE, domain: 'x.test', claimant: 'a' }
|
|
]);
|
|
assert.strictEqual(view.domains.has('x.test'), false);
|
|
}
|
|
|
|
function testApplyMatchesResolver() {
|
|
const domain = 'apply.test';
|
|
const events = [
|
|
{
|
|
type: EVENT_TYPES.CLAIM_UPSERT,
|
|
domain,
|
|
claimant: 'peer-a',
|
|
hash: 'hs://a',
|
|
timestamp: 100,
|
|
ssl: false,
|
|
clients: []
|
|
},
|
|
{
|
|
type: EVENT_TYPES.CLAIM_UPSERT,
|
|
domain,
|
|
claimant: 'peer-b',
|
|
hash: 'hs://b',
|
|
timestamp: 200,
|
|
ssl: false,
|
|
clients: []
|
|
},
|
|
{ type: EVENT_TYPES.VOTE_UPSERT, domain, claimant: 'peer-a', voter: 'v1' },
|
|
{ type: EVENT_TYPES.VOTE_UPSERT, domain, claimant: 'peer-a', voter: 'v2' },
|
|
{ type: EVENT_TYPES.VOTE_UPSERT, domain, claimant: 'peer-a', voter: 'v3' }
|
|
];
|
|
const view = replayEvents(events);
|
|
const state = resolveFromView(view, domain, 'peer-z', 4);
|
|
assert.strictEqual(state.status, 'resolved');
|
|
assert.strictEqual(state.resolvedClaimant, 'peer-a');
|
|
assert.strictEqual(state.hash, 'hs://a');
|
|
}
|
|
|
|
function testKvBootstrapFixture() {
|
|
const entries = [
|
|
{
|
|
key: 'claim:fixture.test:alpha',
|
|
value: JSON.stringify({ hash: 'hs://alpha', timestamp: 10, ssl: false })
|
|
},
|
|
{
|
|
key: 'claim:fixture.test:beta',
|
|
value: JSON.stringify({ hash: 'hs://beta', timestamp: 20, ssl: false })
|
|
},
|
|
{ key: 'vote:fixture.test:alpha:voter-1', value: 'hs://alpha' },
|
|
{ key: 'vote:fixture.test:alpha:voter-2', value: 'hs://alpha' },
|
|
{ key: 'vote:fixture.test:alpha:voter-3', value: 'hs://alpha' }
|
|
];
|
|
const view = replayEvents(entriesToBootstrapEvents(entries));
|
|
const state = resolveFromView(view, 'fixture.test', 'gamma', 4);
|
|
assert.strictEqual(state.status, 'resolved');
|
|
assert.strictEqual(state.resolvedClaimant, 'alpha');
|
|
}
|
|
|
|
function run() {
|
|
testBootstrapOrdering();
|
|
testClaimRemoveDropsVotes();
|
|
testApplyMatchesResolver();
|
|
testKvBootstrapFixture();
|
|
console.log('consensus-apply.test.js: all passed');
|
|
}
|
|
|
|
run();
|