import type { Pool } from 'pg'; import { NETWORK } from '../../shared/network.ts'; import { CHAIN_ID } from '../testnet/consensus.ts'; import { CUSTODY_WRITE_LOCK, verifyCustodySchema } from './postgres-schema.ts'; export type MainnetCustodyIdentity = { version: 1; mode: 'mainnet-custody'; policy: 'custody-mainnet-v1'; network: 'wikideck-randomx-mainnet-1'; chainId: string; id: string; }; function uuid(value: string): boolean { return /^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$/i.test(value); } /** One-time, schema-owner-only identity binding for the separately operated mainnet custody ledger. */ export async function initializeMainnetCustodyIdentity(pool: Pool, custodianId: string): Promise { if (NETWORK !== 'wikideck-randomx-mainnet-1' || !uuid(custodianId)) { throw new Error('Mainnet custody identity requires the mainnet profile and a valid custodian UUID'); } const identity: MainnetCustodyIdentity = { version: 1, mode: 'mainnet-custody', policy: 'custody-mainnet-v1', network: NETWORK, chainId: CHAIN_ID, id: custodianId }; const client = await pool.connect(); try { await client.query('BEGIN'); await client.query("SET LOCAL statement_timeout='10s'; SET LOCAL lock_timeout='3s'; SET LOCAL synchronous_commit=on"); await client.query('SELECT pg_advisory_xact_lock($1::bigint)', [CUSTODY_WRITE_LOCK]); await verifyCustodySchema(client); const current = await client.query("SELECT value FROM wk_custody.custody_meta WHERE key='identity' FOR UPDATE"); if (current.rowCount) { const parsed = JSON.parse(current.rows[0].value) as Partial; if (parsed.version !== identity.version || parsed.mode !== identity.mode || parsed.policy !== identity.policy || parsed.network !== identity.network || parsed.chainId !== identity.chainId || parsed.id !== identity.id) { throw new Error('Custody database is already bound to a different network or custodian identity'); } } else { const populated = await client.query(`SELECT EXISTS(SELECT 1 FROM wk_custody.journals) AS has_journals, EXISTS(SELECT 1 FROM wk_custody.monero_deposit_allocations) AS has_allocations`); if (populated.rows[0].has_journals || populated.rows[0].has_allocations) { throw new Error('Cannot initialize mainnet custody identity after financial or deposit records exist'); } await client.query('INSERT INTO wk_custody.custody_meta(key,value) VALUES($1,$2)', ['identity', JSON.stringify(identity)]); } await client.query('COMMIT'); return identity; } catch (error) { await client.query('ROLLBACK').catch(() => {}); throw error; } finally { client.release(); } } export async function verifyMainnetCustodyIdentity(pool: Pool, custodianId: string): Promise { if (NETWORK !== 'wikideck-randomx-mainnet-1' || !uuid(custodianId)) { throw new Error('Mainnet custody identity requires the mainnet profile and a valid custodian UUID'); } const result = await pool.query("SELECT value FROM wk_custody.custody_meta WHERE key='identity'"); if (result.rowCount !== 1) throw new Error('Mainnet custody identity is not initialized'); const identity = JSON.parse(result.rows[0].value) as Partial; if (identity.version !== 1 || identity.mode !== 'mainnet-custody' || identity.policy !== 'custody-mainnet-v1' || identity.network !== NETWORK || identity.chainId !== CHAIN_ID || identity.id !== custodianId) { throw new Error('Mainnet custody identity does not match configured custodian'); } return identity as MainnetCustodyIdentity; } export function parseMainnetCustodianId(value: string): string { if (!uuid(value)) throw new Error('Invalid mainnet custodian UUID'); return value.toLowerCase(); }