Cold-restore rehearsal

This commit is contained in:
2026-10-20 23:18:14 -04:00
parent 5b21074462
commit 0ac2073038
4 changed files with 30 additions and 13 deletions
+25 -8
View File
@@ -18,7 +18,7 @@ import { migrateCustody, grantCustodyRuntime } from '../../server/market/postgre
import { importSqliteCustody } from '../../server/market/custody-import.ts';
import { postgresConfig, custodyStorage } from '../../server/market/postgres-config.ts';
import { openCustodyStorage } from '../../server/market/custody-storage.ts';
import { createBackup } from '../../server/recovery.ts';
import { createBackup, restoreBackup } from '../../server/recovery.ts';
import { createCoordinatedCustodyBackup, verifyCoordinatedCustodyBackup } from '../../server/coordinated-backup.ts';
import { CUSTODY_POLICY, custodyMessage, type CustodyAction, type CustodyCommand, type CustodyActivity,
type CustodyListing, type CustodyOrder, type CustodyWithdrawal } from '../../shared/custody.ts';
@@ -374,13 +374,22 @@ test('coordinated node/PostgreSQL backup holds both writer fences and verifies m
t.after(() => rm(dir, { recursive: true, force: true }));
const data = join(dir, 'node-data'), networkDir = join(data, 'testnet');
await mkdir(networkDir, { recursive: true });
const ledgerDb = new DatabaseSync(join(networkDir, 'ledger.sqlite'));
try {
ledgerDb.exec(`CREATE TABLE metadata(key TEXT PRIMARY KEY,value TEXT NOT NULL);
INSERT INTO metadata VALUES('schema_version','1'),('network','${NETWORK}'),('chain_id','${CHAIN_ID}');
CREATE TABLE blocks(sequence INTEGER PRIMARY KEY AUTOINCREMENT,hash TEXT NOT NULL,previous_hash TEXT NOT NULL,block_json TEXT NOT NULL);
CREATE TABLE pending_transfers(position INTEGER PRIMARY KEY,id TEXT NOT NULL,transaction_json TEXT NOT NULL);`);
} finally { ledgerDb.close(); }
const nativeEngine = new RandomX({ binary: process.env.RANDOMX_BINARY });
const sourceLedger = await Ledger.open(data, nativeEngine);
const blockTemplate = makeTemplate([], {}, seller.actor, seller.publicKey, [], fixtureArticle(56000), fixtureStats(), GENESIS_TIME + 30);
const header = structuredClone(blockTemplate.header);
let mined: Awaited<ReturnType<typeof nativeEngine.mine>> = null;
for (let batch = 0; batch < 16 && !mined; batch++) {
mined = await nativeEngine.mine(header.key, miningBlob(header), header.target, 32, 64);
if (!mined) header.nonce += 64;
}
assert.ok(mined, 'Native RandomX cold-backup fixture mining exceeded bounded attempts');
header.nonce = mined.nonce;
const blockHash = sha256(miningBlob(header));
const sourceBlock: TestnetBlock = { ...header, hash: blockHash, tokenId: mintId(header), powHash: mined.hash,
signature: seller.sign(blockClaimMessage(blockHash)), transactions: [] };
await sourceLedger.submit(sourceBlock);
sourceLedger.close(); nativeEngine.close();
const marketDb = new DatabaseSync(join(networkDir, 'market.sqlite'));
try {
marketDb.exec('CREATE TABLE market_meta(key TEXT PRIMARY KEY,value TEXT NOT NULL)');
@@ -410,6 +419,14 @@ test('coordinated node/PostgreSQL backup holds both writer fences and verifies m
assert.equal(manifest.sqliteCutoverSourceDigest, cutoverDigest);
assert.equal((await verifyCoordinatedCustodyBackup(destination)).nodeManifestSha256, manifest.nodeManifestSha256);
await assert.rejects(lstat(join(destination, 'node', 'custody-sandbox.sqlite')), { code: 'ENOENT' });
const restoredNodeDir = join(dir, 'restored-node');
await restoreBackup(join(destination, 'node'), restoredNodeDir);
const verifierEngine = new RandomX({ binary: process.env.RANDOMX_BINARY });
const verifierLedger = await Ledger.open(restoredNodeDir, verifierEngine);
try {
assert.equal(verifierLedger.summary().tip, sourceBlock.hash);
assert.equal(verifierLedger.nft(sourceBlock.tokenId)?.owner, seller.actor);
} finally { verifierLedger.close(); verifierEngine.close(); }
const restoreDatabase = `pair_restore_${randomUUID().replaceAll('-', '')}`, run = promisify(execFile);
await f.admin.query(`CREATE DATABASE "${restoreDatabase}"`);
try {