Remove the last layer of soft centralization: a lone `--master` on empty storage could still implicitly genesis a separate Autopass and split the network on the same TOPIC_SEED. Genesis is now explicit (`--master --genesis`); secondary masters pair via invite into the same dnsPass as joiners, with a shared network manifest, split-brain diagnostics, and writer-aware quorum. - Genesis vs secondary master boot paths; auto-adopt manifest on upgrade - Masters without dnsPass accept invites; masters with pass ignore them - NETWORK_MANIFEST_FILE, P2NS_GENESIS, MASTER_LOAD_DOMAINS, MASTER_INVITE_ONLY - core.status networkId; admin diagnostics; docs and multi-master tests BREAKING: operators must run one genesis per network; additional masters use `node p2ns.js --master` (not `--genesis`) on empty storage.
78 lines
2.4 KiB
JavaScript
78 lines
2.4 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Unit tests for network manifest helpers.
|
|
*/
|
|
|
|
const assert = require('assert');
|
|
const fs = require('fs').promises;
|
|
const path = require('path');
|
|
const os = require('os');
|
|
const {
|
|
extractNetworkIdentity,
|
|
validateManifestAgainstPass,
|
|
readManifest,
|
|
writeManifest,
|
|
storageDirHasCorestoreData
|
|
} = require('../includes/infrastructure/network-manifest');
|
|
|
|
function mockPass(discoveryHex, writerHex) {
|
|
const discoveryKey = Buffer.from(discoveryHex, 'hex');
|
|
const writerKey = Buffer.from(writerHex, 'hex');
|
|
return {
|
|
discoveryKey,
|
|
writerKey,
|
|
base: { key: writerKey }
|
|
};
|
|
}
|
|
|
|
async function testStorageDirEmpty() {
|
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'p2ns-manifest-'));
|
|
assert.strictEqual(await storageDirHasCorestoreData(dir), false);
|
|
await fs.writeFile(path.join(dir, 'core'), 'x');
|
|
assert.strictEqual(await storageDirHasCorestoreData(dir), true);
|
|
await fs.rm(dir, { recursive: true, force: true });
|
|
}
|
|
|
|
async function testManifestRoundTrip() {
|
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'p2ns-manifest-'));
|
|
const manifestPath = path.join(dir, 'network.json');
|
|
const pass = mockPass('aa'.repeat(32), 'bb'.repeat(32));
|
|
const identity = extractNetworkIdentity(pass, 'bb'.repeat(64));
|
|
await writeManifest(manifestPath, {
|
|
...identity,
|
|
topicSeed: 'p2ns-dns',
|
|
isGenesis: true
|
|
});
|
|
const read = await readManifest(manifestPath);
|
|
assert.strictEqual(read.networkId, identity.networkId);
|
|
assert.strictEqual(read.topicSeed, 'p2ns-dns');
|
|
const validation = validateManifestAgainstPass(read, pass, 'p2ns-dns');
|
|
assert.strictEqual(validation.ok, true);
|
|
await fs.rm(dir, { recursive: true, force: true });
|
|
}
|
|
|
|
async function testTopicMismatch() {
|
|
const pass = mockPass('cc'.repeat(32), 'dd'.repeat(32));
|
|
const manifest = {
|
|
version: 1,
|
|
networkId: pass.discoveryKey.toString('hex'),
|
|
topicSeed: 'other-seed',
|
|
autopassDiscoveryKey: pass.discoveryKey.toString('hex')
|
|
};
|
|
const validation = validateManifestAgainstPass(manifest, pass, 'p2ns-dns');
|
|
assert.strictEqual(validation.ok, false);
|
|
assert.strictEqual(validation.error, 'topic_seed_mismatch');
|
|
}
|
|
|
|
async function run() {
|
|
await testStorageDirEmpty();
|
|
await testManifestRoundTrip();
|
|
await testTopicMismatch();
|
|
console.log('network-manifest.test.js: all passed');
|
|
}
|
|
|
|
run().catch((err) => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
});
|