import test from 'node:test'; import assert from 'node:assert/strict'; import { Pool, type PoolConfig } from 'pg'; import { createHash, generateKeyPairSync, randomUUID, sign } from 'node:crypto'; import { access, chmod, lstat, mkdtemp, rm, writeFile, mkdir, readFile } from 'node:fs/promises'; import { constants } from 'node:fs'; import { join, resolve } from 'node:path'; import { tmpdir } from 'node:os'; import { spawn } from 'node:child_process'; import { execFile } from 'node:child_process'; import { promisify } from 'node:util'; import { DatabaseSync } from 'node:sqlite'; import { PostgresCustody, auditPostgres } from '../../server/market/postgres-custody.ts'; import { CustodyIntegrityError, auditBusinessLiabilities } from '../../server/market/custody-integrity.ts'; import { resolveCustodyIncident } from '../../server/market/custody-incident.ts'; import { CustodyMarket } from '../../server/market/custody.ts'; import { migrateCustody, grantCustodyRuntime, grantMoneroObserverRuntime, CUSTODY_SCHEMA_V1, CUSTODY_SCHEMA_V1_DIGEST, grantMoneroDepositCreditRuntime, grantMoneroDepositAllocatorRuntime, grantMoneroWithdrawalRuntime, grantPilotDepositReservationRuntime, grantPilotDepositAddressRuntime, grantPilotMoneroObserverRuntime, grantPilotMoneroDepositReviewRuntime, CUSTODY_MIGRATION_V2, CUSTODY_SCHEMA_V2_DIGEST } from '../../server/market/postgres-schema.ts'; import { parseMainnetCustodianId } from '../../server/market/mainnet-custody-identity.ts'; import { initializePilotCustodyIdentity, parsePilotCustodianId } from '../../server/market/pilot-custody-identity.ts'; import { createOrRecoverPilotMoneroDepositAddress, reservePilotMoneroDepositRequest, reservePilotMoneroDepositIndex } from '../../server/market/pilot-monero-deposit-allocation.ts'; import { persistPilotMoneroObservationScan } from '../../server/market/pilot-monero-observation-store.ts'; import { readMainnetCustodyBalance, reserveMainnetWithdrawal, cancelMainnetWithdrawal } from '../../server/market/mainnet-withdrawal.ts'; import { mainnetCustodyBalanceMessage, mainnetWithdrawalCancellationMessage, mainnetWithdrawalMessage, type MainnetCustodyBalanceRequestUnsigned, type MainnetWithdrawalRequestUnsigned, type MainnetWithdrawalCancellationUnsigned } from '../../shared/monero-withdrawal.ts'; import { persistMoneroObservationScan } from '../../server/market/monero-observation-store.ts'; import { moneroWalletFingerprint } from '../../server/market/monero-observer.ts'; import { runMoneroObserverBatch, runMoneroObserverWorker } from '../../server/market/monero-observer-worker.ts'; import { depositCreditApprovalMessage, depositCreditReleaseApprovalMessage, depositReorgResolutionMessage, postReviewedDepositPendingCredit, prepareDepositCreditReleaseReview, prepareDepositCreditReview, prepareDepositReorgResolution, reconcileDepositCreditReorgHolds, releaseReviewedDepositCredit, resolveReleasedDepositReorgHold, type DepositCreditPolicy } from '../../server/market/monero-deposit-credit.ts'; import { createOrRecoverMoneroDepositSubaddress, registerMoneroDepositSubaddress, reserveMoneroDepositAllocation } from '../../server/market/monero-deposit-allocation-store.ts'; import { enrollMoneroDepositAddress } from '../../server/market/monero-deposit-enrollment.ts'; 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, 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'; import { moneroDepositAddressMessage, type MoneroDepositAddressRequestUnsigned } from '../../shared/monero-deposit.ts'; import { ADDITIONAL_MARKET_FEE } from '../../shared/market.ts'; import { NETWORK } from '../../shared/network.ts'; import { CHAIN_ID, GENESIS_HASH, address, sha256 } from '../../server/testnet/consensus.ts'; import { saleContext, termsForCustodyOrder, saleCandidateTransactionId, saleId } from '../../server/testnet/sale-candidate.ts'; import { SALE_CANDIDATE, saleTermsMessage, saleCompletionMessage, saleCancellationMessage, type SaleTransaction } from '../../shared/sale-candidate.ts'; import { replayReservedSale, type SaleRehearsal } from '../../server/market/sale-rehearsal.ts'; import { keccak } from 'hash-wasm'; import type { CanonicalInboxPage } from '../../server/market/canonical-inbox.ts'; import { CanonicalObserver } from '../../server/market/canonical-observer.ts'; import { Ledger } from '../../server/testnet/ledger.ts'; import { RandomX } from '../../server/testnet/randomx.ts'; import { GENESIS_TIME, makeTemplate, miningBlob, mintId, transition } from '../../server/testnet/consensus.ts'; import { deriveStats } from '../../shared/enrichment.ts'; import { blockClaimMessage, type NftStateView, type TestnetBlock } from '../../shared/testnet.ts'; if (process.env.WIKIDECK_PG_DISPOSABLE_TEST !== '1') throw new Error('PostgreSQL tests require the isolated disposable compose fixture'); const config: PoolConfig = { host: '127.0.0.1', database: 'custody_test', user: 'postgres', ssl: false, max: 4, connectionTimeoutMillis: 5000, statement_timeout: 10000, lock_timeout: 3000 }; function identity() { const keys = generateKeyPairSync('ec', { namedCurve: 'prime256v1' }); const publicKey = keys.publicKey.export({ type: 'spki', format: 'der' }).toString('base64'); return { publicKey, actor: address(publicKey), sign: (message: string) => sign('sha256', Buffer.from(message), { key: keys.privateKey, dsaEncoding: 'ieee-p1363' }).toString('base64') }; } const buyer = identity(), seller = identity(), other = identity(), hash = 'a'.repeat(64); const host = identity(); async function syntheticFeeAddress() { const prefix = Uint8Array.from([18, ...Array(64).fill(2)]); const data = Buffer.concat([prefix, Buffer.from((await keccak(prefix, 256)).slice(0, 8), 'hex')]); const alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; let encoded = ''; for (let i = 0; i < data.length; i += 8) { const chunk = data.subarray(i, i + 8); let n = BigInt(`0x${chunk.toString('hex')}`), text = ''; while (n) { text = alphabet[Number(n % 58n)] + text; n /= 58n; } encoded += text.padStart(chunk.length === 8 ? 11 : 7, '1'); } return encoded; } async function syntheticSubaddress(fill = 3) { const prefix = Uint8Array.from([42, ...Array(64).fill(fill)]); const data = Buffer.concat([prefix, Buffer.from((await keccak(prefix, 256)).slice(0, 8), 'hex')]); const alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; const sizes: Record = { 8: 11, 5: 7 }; let encoded = ''; for (let i = 0; i < data.length; i += 8) { const chunk = data.subarray(i, i + 8); let n = BigInt(`0x${chunk.toString('hex')}`), text = ''; while (n) { text = alphabet[Number(n % 58n)] + text; n /= 58n; } encoded += text.padStart(sizes[chunk.length], '1'); } return encoded; } function saleTranscript(order: CustodyOrder, custodianId: string, feeBps = 250) { const policy = { coinbaseMaturity: 2, lockDepth: 2, outcomeDepth: 3, maxLockBlocks: 30, maxTransactions: 64 }; const context = saleContext(policy); const terms = termsForCustodyOrder(order, context, { custodianId, host: host.actor, feeBps, lockUntilHeight: 12, expiresHeight: 20 }); const authorization = (who: typeof buyer, message: string) => ({ publicKey: who.publicKey, signature: who.sign(message) }); const lock: Extract = { type: 'sale-lock', terms, sellerAuthorization: authorization(seller, saleTermsMessage(terms)), buyerAuthorization: authorization(buyer, saleTermsMessage(terms)) }; const input: SaleRehearsal = { policy, seed: { height: 9, mintedHeight: 1, pageId: 1 }, lock, blocks: [[lock]] }; const reservationDigest = replayReservedSale(order, custodianId, input, feeBps).reservationDigest; const reference = { protocol: SALE_CANDIDATE, chainId: context.chainId, saleId: saleId(terms) } as const; const completion = { ...reference, reservationDigest }; const complete: SaleTransaction = { type: 'sale-complete', ...completion, hostAuthorization: authorization(host, saleCompletionMessage(completion)) }; const cancel: SaleTransaction = { type: 'sale-cancel', ...reference, hostAuthorization: authorization(host, saleCancellationMessage(reference)), buyerAuthorization: authorization(buyer, saleCancellationMessage(reference)) }; return { input, complete, cancel, reservationDigest }; } function canonicalPage(streamId: string, all: CanonicalInboxPage['events'], start: number, count: number): CanonicalInboxPage { const events = all.slice(start, start + count), cursor = events.at(-1)?.sequence ?? start; const previousAnchor = start === 0 ? GENESIS_HASH : all[start - 1].eventHash; const anchor = events.at(-1)?.eventHash ?? previousAnchor; return { streamId, previousAnchor, anchor, events, cursor, head: all.length, caughtUp: cursor === all.length }; } function canonicalEvents(streamId: string, linkedTxId = '1'.repeat(64)) { const rows: CanonicalInboxPage['events'] = []; let anchor = GENESIS_HASH; const add = (type: 'connect' | 'disconnect', height: number, hash: string, previousHash: string, transactionIds: string[] = []) => { const sequence = rows.length + 1; const eventHash = sha256(JSON.stringify(['wikideck-canonical-event-v1', streamId, sequence, type, height, hash, previousHash, transactionIds, anchor])); rows.push({ sequence, type, height, hash, previousHash, transactionIds, eventHash }); anchor = eventHash; }; const a = 'a'.repeat(64), b = 'b'.repeat(64), c = 'c'.repeat(64); add('connect', 1, a, GENESIS_HASH, [linkedTxId]); add('disconnect', 1, a, GENESIS_HASH, [linkedTxId]); add('connect', 1, b, GENESIS_HASH, [linkedTxId]); add('connect', 2, c, b, ['3'.repeat(64)]); return rows; } function reanchorCanonicalEvents(streamId: string, rows: CanonicalInboxPage['events']) { let anchor = GENESIS_HASH; for (const event of rows) { event.eventHash = sha256(JSON.stringify(['wikideck-canonical-event-v1', streamId, event.sequence, event.type, event.height, event.hash, event.previousHash, event.transactionIds, anchor])); anchor = event.eventHash; } } class FixturePow { async hash(key: string, blob: Uint8Array): Promise { return sha256(Buffer.concat([Buffer.from(key, 'hex'), Buffer.from(blob)])); } close(): void {} } class EasyFixturePow extends FixturePow { async hash(_key: string, _blob: Uint8Array): Promise { return '0'.repeat(64); } } function fixtureArticle(pageId = 50000) { return { pageId, title: 'Canonical inbox fixture', extract: `Synthetic local-only canonical observer test fixture ${pageId}.`, image: 'https://upload.wikimedia.org/wikipedia/commons/test-image.jpg', url: `https://en.wikipedia.org/?curid=${pageId}`, revisionId: 1, source: 'live' as const, category: 'Culture' }; } function fixtureStats() { const pageBytes = 2048, languageCount = 3; return { capturedAt: '2026-10-19T00:00:00.000Z', revisionId: 1, pageBytes, languageCount, languagesTruncated: false, pageviews: { status: 'unavailable' as const, months: [], latestMonth: '2026-09', total: null, dailyAverage: null, trendPercent: null }, stats: deriveStats(pageBytes, languageCount, null) }; } async function fixtureBlock(ancestors: TestnetBlock[], state: NftStateView, pageId = 50000, proof = new FixturePow()) { const miner = seller; const template = makeTemplate(ancestors, state, miner.actor, miner.publicKey, [], fixtureArticle(pageId), fixtureStats(), GENESIS_TIME + (ancestors.length + 1) * 30); const header = structuredClone(template.header); for (header.nonce = 0; header.nonce < 100000; header.nonce++) { const blob = miningBlob(header), powHash = await proof.hash(header.key, blob); if (BigInt(`0x${powHash}`) > BigInt(`0x${header.target}`)) continue; const hash = sha256(blob); return { ...header, hash, tokenId: mintId(header), powHash, signature: miner.sign(blockClaimMessage(hash)), transactions: [] } satisfies TestnetBlock; } throw new Error('Canonical fixture nonce search exhausted'); } const options = { feeBps: 250, card: (token: string, owner: string, sequence: number) => token === hash && owner === seller.actor && sequence === 0 ? { title: 'PostgreSQL fixture' } : undefined }; function envelope(id: string, who: typeof buyer, action: CustodyAction, nonce: string = randomUUID()): CustodyCommand { const c: Omit = { version: 1, mode: 'sandbox', policy: CUSTODY_POLICY, network: NETWORK, chainId: CHAIN_ID, custodianId: id, actor: who.actor, publicKey: who.publicKey, nonce, expiresAt: Date.now() + 240000, action }; return { ...c, signature: who.sign(custodyMessage(c)) }; } async function fixture(t: { after(fn: () => Promise): void }) { const admin = new Pool(config); await admin.query('DROP SCHEMA IF EXISTS wk_custody CASCADE'); // This database exists ONLY in isolated tmpfs fixture. await migrateCustody(admin); await admin.query('DO $$ BEGIN IF NOT EXISTS(SELECT 1 FROM pg_roles WHERE rolname=\'custody_runtime\') THEN CREATE ROLE custody_runtime LOGIN; END IF; END $$'); await grantCustodyRuntime(admin, 'custody_runtime'); const runtime = { ...config, user: 'custody_runtime' }; const market = await PostgresCustody.open(runtime, options); t.after(async () => { await market.close(); await admin.end(); }); const execute = (who: typeof buyer, a: CustodyAction) => market.execute(envelope(market.id, who, a)); const activity = async (who = buyer) => await execute(who, { type: 'activity', offset: 0 }) as CustodyActivity; return { admin, runtime, market, execute, activity }; } test('explicit PostgreSQL migrations are idempotent and runtime role cannot alter financial history or schema', async t => { const f = await fixture(t); await migrateCustody(f.admin); await assert.rejects(PostgresCustody.open(config, options), /non-administrative/); await f.execute(buyer, { type: 'mock-credit' }); const runtime = new Pool(f.runtime); try { for (const sql of ["UPDATE wk_custody.accounts SET balance=999", 'DELETE FROM wk_custody.entries', 'TRUNCATE wk_custody.journals', "ALTER TABLE wk_custody.accounts ADD COLUMN bad text", "UPDATE wk_custody.receipts SET digest='bad'"]) await assert.rejects(runtime.query(sql)); await assert.rejects(grantCustodyRuntime(f.admin, 'postgres'), /administrative/); await assert.rejects(grantCustodyRuntime(f.admin, 'bad;DROP SCHEMA'), /identifier/); } finally { await runtime.end(); } assert.equal((await f.market.audit()).assetsAtomicXmr, '1000000000000'); }); test('custody schema v1 upgrades additively through v14 without dropping existing metadata', async t => { const admin = new Pool(config); await admin.query('DROP SCHEMA IF EXISTS wk_custody CASCADE'); const client = await admin.connect(); try { await client.query('BEGIN'); await client.query(CUSTODY_SCHEMA_V1); await client.query('INSERT INTO wk_custody.schema_versions VALUES(1,$1)', [CUSTODY_SCHEMA_V1_DIGEST]); await client.query('INSERT INTO wk_custody.custody_meta VALUES($1,$2)', ['v1-preserved-marker', 'existing-test-data']); await client.query('COMMIT'); } catch (error) { await client.query('ROLLBACK'); throw error; } finally { client.release(); } try { await migrateCustody(admin); await migrateCustody(admin); const versions = await admin.query('SELECT version FROM wk_custody.schema_versions ORDER BY version'); assert.deepEqual(versions.rows.map(row => Number(row.version)), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]); assert.equal((await admin.query("SELECT value FROM wk_custody.custody_meta WHERE key='v1-preserved-marker'")).rows[0].value, 'existing-test-data'); for (const table of ['monero_observer_state', 'monero_transfer_observations', 'monero_observation_events', 'monero_observation_scans']) { assert.ok((await admin.query('SELECT to_regclass($1) AS name', [`wk_custody.${table}`])).rows[0].name); } assert.ok((await admin.query("SELECT to_regclass('wk_custody.monero_deposit_allocations') AS name")).rows[0].name); assert.ok((await admin.query("SELECT to_regclass('wk_custody.pilot_custody_identity') AS name")).rows[0].name); assert.ok((await admin.query("SELECT to_regclass('wk_custody.pilot_monero_deposit_reservations') AS name")).rows[0].name); assert.ok((await admin.query("SELECT to_regclass('wk_custody.pilot_monero_observation_events') AS name")).rows[0].name); for (const table of ['monero_deposit_credit_events', 'monero_deposit_reorg_holds', 'monero_deposit_release_events', 'monero_deposit_reorg_resolutions']) { assert.ok((await admin.query('SELECT to_regclass($1) AS name', [`wk_custody.${table}`])).rows[0].name); } } finally { await admin.end(); } }); test('custody schema v2 upgrades explicitly through v14 deposit and withdrawal lifecycle tables', async t => { const admin = new Pool(config); await admin.query('DROP SCHEMA IF EXISTS wk_custody CASCADE'); const client = await admin.connect(); try { await client.query('BEGIN'); await client.query(CUSTODY_SCHEMA_V1); await client.query('INSERT INTO wk_custody.schema_versions VALUES(1,$1)', [CUSTODY_SCHEMA_V1_DIGEST]); await client.query(CUSTODY_MIGRATION_V2); await client.query('INSERT INTO wk_custody.schema_versions VALUES(2,$1)', [CUSTODY_SCHEMA_V2_DIGEST]); await client.query("INSERT INTO wk_custody.custody_meta VALUES('v2-marker','preserve')"); await client.query('COMMIT'); await migrateCustody(admin); await migrateCustody(admin); const versions = await admin.query('SELECT version FROM wk_custody.schema_versions ORDER BY version'); assert.deepEqual(versions.rows.map(row => Number(row.version)), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]); assert.equal((await admin.query("SELECT value FROM wk_custody.custody_meta WHERE key='v2-marker'")).rows[0].value, 'preserve'); assert.ok((await admin.query("SELECT to_regclass('wk_custody.monero_deposit_release_events') AS name")).rows[0].name); assert.ok((await admin.query("SELECT to_regclass('wk_custody.monero_withdrawal_intents') AS name")).rows[0].name); assert.ok((await admin.query("SELECT to_regclass('wk_custody.pilot_custody_identity') AS name")).rows[0].name); assert.ok((await admin.query("SELECT to_regclass('wk_custody.pilot_monero_deposit_reservations') AS name")).rows[0].name); } catch (error) { await client.query('ROLLBACK').catch(() => {}); throw error; } finally { client.release(); await admin.end(); } }); test('pilot custody identity binds the private market chain to Monero Mainnet without enabling funds', async t => { assert.equal(parsePilotCustodianId('AAAAAAAA-AAAA-4AAA-8AAA-AAAAAAAAAAAA'), 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa'); assert.throws(() => parsePilotCustodianId('not-a-uuid'), /Invalid pilot custodian UUID/); const admin = new Pool(config), database = `pilot_identity_${randomUUID().replaceAll('-', '')}`; await admin.query(`CREATE DATABASE "${database}"`); const target = new Pool({ ...config, database }); const run = promisify(execFile), custodianId = randomUUID(), runtimeRole = `pilot_identity_${randomUUID().replaceAll('-', '')}`; const code = `import { Pool } from 'pg'; import { initializePilotCustodyIdentity } from './server/market/pilot-custody-identity.ts'; const pool=new Pool({host:'127.0.0.1',database:process.env.TEST_DATABASE,user:'postgres',ssl:false}); try { console.log(JSON.stringify(await initializePilotCustodyIdentity(pool,process.env.TEST_CUSTODIAN_ID,1234))); } finally { await pool.end(); }`; const env = { ...process.env, WIKIDECK_NETWORK: 'pilot', WIKIDECK_PILOT_MODE: 'private-mock-only', TEST_DATABASE: database, TEST_CUSTODIAN_ID: custodianId }; try { await migrateCustody(target); await admin.query(`CREATE ROLE "${runtimeRole}" LOGIN`); assert.equal((await target.query('SELECT has_table_privilege($1,$2,$3) AS allowed', [runtimeRole, 'wk_custody.pilot_custody_identity', 'SELECT'])).rows[0].allowed, false); const first = JSON.parse((await run(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', code], { env })).stdout.trim()); const replay = JSON.parse((await run(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', code], { env })).stdout.trim()); assert.equal(first.mode, 'pilot-custody'); assert.equal(first.marketNetwork, 'wikideck-randomx-pilot-1'); assert.equal(first.moneroNetwork, 'mainnet'); assert.equal(first.activationStatus, 'identity-only'); assert.equal(first.custodianId, custodianId); assert.deepEqual(replay, first); await assert.rejects(run(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', code], { env: { ...env, TEST_CUSTODIAN_ID: randomUUID() }, })); await assert.rejects(run(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', code], { env: { ...env, WIKIDECK_NETWORK: 'testnet' }, })); const stored = await target.query('SELECT * FROM wk_custody.pilot_custody_identity'); assert.equal(stored.rowCount, 1); assert.equal((await target.query('SELECT count(*)::int AS n FROM wk_custody.journals')).rows[0].n, 0); assert.equal((await target.query('SELECT count(*)::int AS n FROM wk_custody.monero_deposit_allocations')).rows[0].n, 0); assert.equal((await target.query('SELECT count(*)::int AS n FROM wk_custody.monero_withdrawal_intents')).rows[0].n, 0); assert.equal((await target.query("SELECT count(*)::int AS n FROM wk_custody.custody_meta WHERE key='identity'")).rows[0].n, 0); } finally { await target.end(); await admin.query(`DROP DATABASE IF EXISTS "${database}" WITH (FORCE)`); await admin.query(`DROP ROLE IF EXISTS "${runtimeRole}"`); await admin.end(); } }); test('pilot custody identity refuses a database containing pre-existing mock or financial data', async t => { const admin = new Pool(config), database = `pilot_identity_dirty_${randomUUID().replaceAll('-', '')}`; await admin.query(`CREATE DATABASE "${database}"`); const target = new Pool({ ...config, database }), run = promisify(execFile); try { await migrateCustody(target); await target.query("INSERT INTO wk_custody.accounts(id,normal) VALUES('pre-existing-mock','credit')"); const code = `import { Pool } from 'pg'; import { initializePilotCustodyIdentity } from './server/market/pilot-custody-identity.ts'; const p=new Pool({host:'127.0.0.1',database:process.env.TEST_DATABASE,user:'postgres',ssl:false}); try { await initializePilotCustodyIdentity(p,process.env.TEST_CUSTODIAN_ID,1234); } finally { await p.end(); }`; await assert.rejects(run(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', code], { env: { ...process.env, WIKIDECK_NETWORK: 'pilot', WIKIDECK_PILOT_MODE: 'private-mock-only', TEST_DATABASE: database, TEST_CUSTODIAN_ID: randomUUID() }, }), /separate empty custody database/); assert.equal((await target.query('SELECT count(*)::int AS n FROM wk_custody.pilot_custody_identity')).rows[0].n, 0); } finally { await target.end(); await admin.query(`DROP DATABASE IF EXISTS "${database}" WITH (FORCE)`); await admin.end(); } }); test('pilot signed deposit requests reserve unique durable indexes, replay safely, and cannot access funds or addresses', async t => { const admin = new Pool(config), database = `pilot_reservations_${randomUUID().replaceAll('-', '')}`; const role = `pilot_allocator_${randomUUID().replaceAll('-', '')}`; await admin.query(`CREATE DATABASE "${database}"`); const owner = new Pool({ ...config, database }), run = promisify(execFile); const script = `import { Pool } from 'pg'; import { generateKeyPairSync, sign, randomUUID } from 'node:crypto'; import { address, CHAIN_ID } from './server/testnet/consensus.ts'; import { pilotMoneroDepositRequestMessage } from './shared/pilot-monero-deposit.ts'; import { reservePilotMoneroDepositRequest } from './server/market/pilot-monero-deposit-allocation.ts'; const pool=new Pool({host:'127.0.0.1',database:process.env.DB,user:process.env.DB_ROLE,ssl:false,max:4}); const generation='bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb', fingerprint='c'.repeat(64), now=1800000000000; function newPair() { return generateKeyPairSync('ec',{namedCurve:'prime256v1'}); } function request(pair, accountIndex=0) { const publicKey=pair.publicKey.export({type:'spki',format:'der'}).toString('base64'), beneficiary=address(publicKey); const unsigned={version:1,marketNetwork:'wikideck-randomx-pilot-1',marketChainId:CHAIN_ID,moneroNetwork:'mainnet', custodianId:process.env.CUSTODIAN,beneficiary,publicKey,requestId:randomUUID(),walletGenerationId:generation, walletFingerprint:fingerprint,accountIndex,expiresAt:now+60000}; return {...unsigned,signature:sign('sha256',Buffer.from(pilotMoneroDepositRequestMessage(unsigned)), {key:pair.privateKey,dsaEncoding:'ieee-p1363'}).toString('base64')}; } try { const pairA=newPair(), a=request(pairA); const first=await reservePilotMoneroDepositRequest(pool,{walletGenerationId:generation,walletFingerprint:fingerprint,accountIndex:0,now:()=>now},a,now); const replay=await reservePilotMoneroDepositRequest(pool,{walletGenerationId:generation,walletFingerprint:fingerprint,accountIndex:0,now:()=>now},a,now); const fresh=await reservePilotMoneroDepositRequest(pool,{walletGenerationId:generation,walletFingerprint:fingerprint,accountIndex:0,now:()=>now},request(pairA),now); const b=request(newPair()), c=request(newPair()); const concurrent=await Promise.all([b,c].map(r=>reservePilotMoneroDepositRequest(pool, {walletGenerationId:generation,walletFingerprint:fingerprint,accountIndex:0,now:()=>now},r,now))); const conflict=request(pairA,1); let conflictRejected=false; try { await reservePilotMoneroDepositRequest(pool, {walletGenerationId:generation,walletFingerprint:fingerprint,accountIndex:1,now:()=>now},conflict,now); } catch { conflictRejected=true; } console.log(JSON.stringify({first,replay,fresh,concurrent,conflictRejected})); } finally { await pool.end(); }`; const custodianId = randomUUID(); const bootstrap = `import { Pool } from 'pg'; import { initializePilotCustodyIdentity } from './server/market/pilot-custody-identity.ts'; const pool=new Pool({host:'127.0.0.1',database:process.env.DB,user:'postgres',ssl:false}); try { await initializePilotCustodyIdentity(pool,process.env.CUSTODIAN,1234); } finally { await pool.end(); }`; const env = { ...process.env, WIKIDECK_NETWORK: 'pilot', WIKIDECK_PILOT_MODE: 'private-mock-only', DB: database, CUSTODIAN: custodianId }; try { await migrateCustody(owner); await run(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', bootstrap], { env }); await admin.query(`CREATE ROLE "${role}" LOGIN`); await grantPilotDepositReservationRuntime(owner, role); assert.equal((await owner.query("SELECT has_table_privilege($1,'wk_custody.schema_versions','SELECT') AS allowed", [role])).rows[0].allowed, true); assert.equal((await owner.query("SELECT has_table_privilege($1,'wk_custody.custody_meta','SELECT') AS allowed", [role])).rows[0].allowed, false); const result = JSON.parse((await run(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', script], { env: { ...env, DB_ROLE: role }, })).stdout.trim()); assert.equal(result.first.reservation.addressIndex, 0); assert.equal(result.first.replayed, false); assert.equal(result.replay.reservation.allocationId, result.first.reservation.allocationId); assert.equal(result.replay.replayed, true); assert.equal(result.fresh.reservation.allocationId, result.first.reservation.allocationId); assert.equal(result.fresh.replayed, true); const indexes = result.concurrent.map((entry: { reservation: { addressIndex: number } }) => entry.reservation.addressIndex).sort(); assert.deepEqual(indexes, [1, 2]); assert.ok(result.concurrent.every((entry: { replayed: boolean }) => !entry.replayed)); assert.equal(result.conflictRejected, true); assert.equal(Object.hasOwn(result.first.reservation, 'address'), false); assert.equal(Object.hasOwn(result.first.reservation, 'moneroAddress'), false); assert.equal((await owner.query('SELECT count(*)::int AS n FROM wk_custody.pilot_monero_deposit_reservations')).rows[0].n, 3); assert.equal((await owner.query('SELECT count(*)::int AS n FROM wk_custody.journals')).rows[0].n, 0); assert.equal((await owner.query('SELECT count(*)::int AS n FROM wk_custody.monero_deposit_allocations')).rows[0].n, 0); assert.equal((await owner.query("SELECT has_table_privilege($1,'wk_custody.accounts','SELECT') AS allowed", [role])).rows[0].allowed, false); assert.equal((await owner.query("SELECT has_table_privilege($1,'wk_custody.monero_withdrawal_intents','SELECT') AS allowed", [role])).rows[0].allowed, false); const runtime = new Pool({ ...config, database, user: role }); try { await assert.rejects(runtime.query('UPDATE wk_custody.pilot_monero_deposit_reservations SET address_index=9')); await assert.rejects(runtime.query('DELETE FROM wk_custody.pilot_monero_deposit_reservations')); await assert.rejects(runtime.query('SELECT * FROM wk_custody.accounts')); } finally { await runtime.end(); } } finally { await owner.end(); await admin.query(`DROP DATABASE IF EXISTS "${database}" WITH (FORCE)`); await admin.query(`DROP ROLE IF EXISTS "${role}"`); await admin.end(); } }); test('pilot Wallet-RPC address creation recovers a lost response and registers only the reserved index once', async t => { const admin = new Pool(config), database = `pilot_address_create_${randomUUID().replaceAll('-', '')}`; const role = `pilot_address_${randomUUID().replaceAll('-', '')}`; await admin.query(`CREATE DATABASE "${database}"`); const owner = new Pool({ ...config, database }), run = promisify(execFile), custodianId = randomUUID(); const now = 1_800_000_000_000; const address = await syntheticSubaddress(17); const bootstrap = `import { Pool } from 'pg'; import { initializePilotCustodyIdentity } from './server/market/pilot-custody-identity.ts'; const p=new Pool({host:'127.0.0.1',database:process.env.DB,user:'postgres',ssl:false}); try { await initializePilotCustodyIdentity(p,process.env.CUSTODIAN,Number(process.env.NOW)-1); } finally { await p.end(); }`; const script = `import { Pool } from 'pg'; import { generateKeyPairSync, sign, randomUUID } from 'node:crypto'; import { address as ownerAddress, CHAIN_ID } from './server/testnet/consensus.ts'; import { pilotMoneroDepositRequestMessage } from './shared/pilot-monero-deposit.ts'; import { createOrRecoverPilotMoneroDepositAddress } from './server/market/pilot-monero-deposit-allocation.ts'; const pool=new Pool({host:'127.0.0.1',database:process.env.DB,user:process.env.DB_ROLE,ssl:false,max:4}); const now=Number(process.env.NOW), generation='bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb', fingerprint='c'.repeat(64); function newPair(){return generateKeyPairSync('ec',{namedCurve:'prime256v1'});} function makeRequest(pair,accountIndex=0){ const publicKey=pair.publicKey.export({type:'spki',format:'der'}).toString('base64'); const unsigned={version:1,marketNetwork:'wikideck-randomx-pilot-1',marketChainId:CHAIN_ID,moneroNetwork:'mainnet', custodianId:process.env.CUSTODIAN,beneficiary:ownerAddress(publicKey),publicKey,requestId:randomUUID(), walletGenerationId:generation,walletFingerprint:fingerprint,accountIndex,expiresAt:now+60000}; return {...unsigned,signature:sign('sha256',Buffer.from(pilotMoneroDepositRequestMessage(unsigned)), {key:pair.privateKey,dsaEncoding:'ieee-p1363'}).toString('base64')}; } const request=makeRequest(newPair()); const addresses=new Map(), counters={create:0,verify:0}, failFirstCreate={value:true}; const rpc={verifyWallet:async()=>{counters.verify++;return fingerprint;}, getSubaddress:async(account,index)=>addresses.get(account+':'+index)??null, createSubaddress:async(account,label)=>{counters.create++;if(account===1)throw Error('ambiguous create with no visible address'); const entry={address:process.env.MONERO_ADDRESS,label}; addresses.set(account+':0',entry);if(failFirstCreate.value){failFirstCreate.value=false;throw Error('simulated lost response');} return {address:entry.address,addressIndex:0};}}; try { let firstRejected=false;try{await createOrRecoverPilotMoneroDepositAddress(pool,rpc, {walletGenerationId:generation,walletFingerprint:fingerprint,accountIndex:0,now:()=>now},request,now);}catch{firstRejected=true;} const afterFailure=await pool.query('SELECT status,create_started_at,monero_address FROM wk_custody.pilot_monero_deposit_reservations WHERE request_id=$1',[request.requestId]); const recovered=await createOrRecoverPilotMoneroDepositAddress(pool,rpc, {walletGenerationId:generation,walletFingerprint:fingerprint,accountIndex:0,now:()=>now},request,now); const replay=await createOrRecoverPilotMoneroDepositAddress(pool,rpc, {walletGenerationId:generation,walletFingerprint:fingerprint,accountIndex:0,now:()=>now},request,now); const uncertainRequest=makeRequest(newPair(),1); let uncertainFirst=false,uncertainRetry=false; try{await createOrRecoverPilotMoneroDepositAddress(pool,rpc, {walletGenerationId:generation,walletFingerprint:fingerprint,accountIndex:1,now:()=>now},uncertainRequest,now);}catch{uncertainFirst=true;} try{await createOrRecoverPilotMoneroDepositAddress(pool,rpc, {walletGenerationId:generation,walletFingerprint:fingerprint,accountIndex:1,now:()=>now},uncertainRequest,now);}catch(error){uncertainRetry=/outcome is uncertain/.test(error.message);} const uncertainState=await pool.query('SELECT status FROM wk_custody.pilot_monero_deposit_reservations WHERE request_id=$1',[uncertainRequest.requestId]); const result={firstRejected,afterFailure:afterFailure.rows[0],recovered,replay,uncertainFirst,uncertainRetry, uncertainStatus:uncertainState.rows[0]?.status,counters}; console.log(JSON.stringify(result)); } finally { await pool.end(); }`; const env = { ...process.env, WIKIDECK_NETWORK: 'pilot', WIKIDECK_PILOT_MODE: 'private-mock-only', DB: database, CUSTODIAN: custodianId, NOW: String(now), MONERO_ADDRESS: address }; try { await migrateCustody(owner); await run(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', bootstrap], { env: { ...process.env, WIKIDECK_NETWORK: 'pilot', WIKIDECK_PILOT_MODE: 'private-mock-only', DB: database, CUSTODIAN: custodianId, NOW: String(now) }, }); await admin.query(`CREATE ROLE "${role}" LOGIN`); await grantPilotDepositAddressRuntime(owner, role); assert.equal((await owner.query("SELECT has_table_privilege($1,'wk_custody.pilot_monero_deposit_reservations','UPDATE') AS allowed", [role])).rows[0].allowed, false); assert.equal((await owner.query("SELECT has_column_privilege($1,'wk_custody.pilot_monero_deposit_reservations','status','UPDATE') AS allowed", [role])).rows[0].allowed, true); assert.equal((await owner.query("SELECT has_column_privilege($1,'wk_custody.pilot_monero_deposit_reservations','monero_address','UPDATE') AS allowed", [role])).rows[0].allowed, true); assert.equal((await owner.query("SELECT has_column_privilege($1,'wk_custody.pilot_monero_deposit_reservations','address_index','UPDATE') AS allowed", [role])).rows[0].allowed, false); assert.equal((await owner.query("SELECT has_table_privilege($1,'wk_custody.accounts','SELECT') AS allowed", [role])).rows[0].allowed, false); const result = JSON.parse((await run(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', script], { env: { ...env, DB_ROLE: role }, })).stdout.trim()); assert.equal(result.firstRejected, true); assert.equal(result.afterFailure.status, 'creating'); assert.notEqual(result.afterFailure.create_started_at, null); assert.equal(result.afterFailure.monero_address, null); assert.equal(result.recovered.address, address); assert.equal(result.recovered.replayed, false); assert.equal(result.replay.address, address); assert.equal(result.replay.replayed, true); assert.equal(result.uncertainFirst, true); assert.equal(result.uncertainRetry, true); assert.equal(result.uncertainStatus, 'creating'); assert.equal(result.counters.create, 2, 'ambiguous create must never be retried automatically'); assert.equal((await owner.query("SELECT count(*)::int AS n FROM wk_custody.pilot_monero_deposit_reservations WHERE status='registered' AND monero_address=$1", [address])).rows[0].n, 1); const runtime = new Pool({ ...config, database, user: role }); try { await assert.rejects(runtime.query("UPDATE wk_custody.pilot_monero_deposit_reservations SET monero_address=$1", ['5'.repeat(95)])); await assert.rejects(runtime.query('DELETE FROM wk_custody.pilot_monero_deposit_reservations')); } finally { await runtime.end(); } } finally { await owner.end(); await admin.query(`DROP DATABASE IF EXISTS "${database}" WITH (FORCE)`); await admin.query(`DROP ROLE IF EXISTS "${role}"`); await admin.end(); } }); test('pilot observation persistence classifies allocated and suspense outputs and records reorg evidence only', async t => { const admin = new Pool(config), database = `pilot_observations_${randomUUID().replaceAll('-', '')}`; const role = `pilot_observer_${randomUUID().replaceAll('-', '')}`; await admin.query(`CREATE DATABASE "${database}"`); const owner = new Pool({ ...config, database }), run = promisify(execFile); const custodianId = randomUUID(), generation = randomUUID(), fingerprint = 'c'.repeat(64), now = 1_800_000_000_000; const allocatedAddress = await syntheticSubaddress(21), suspenseAddress = await syntheticSubaddress(22); const code = `import { Pool } from 'pg'; import { randomUUID } from 'node:crypto'; import { CHAIN_ID } from './server/testnet/consensus.ts'; import { initializePilotCustodyIdentity } from './server/market/pilot-custody-identity.ts'; import { reservePilotMoneroDepositIndex } from './server/market/pilot-monero-deposit-allocation.ts'; import { persistPilotMoneroObservationScan } from './server/market/pilot-monero-observation-store.ts'; const config={host:'127.0.0.1',database:process.env.DB,user:'postgres',ssl:false,max:4}; const owner=new Pool(config), observer=new Pool({...config,user:process.env.OBSERVER_ROLE}); const now=Number(process.env.NOW), walletGenerationId=process.env.GENERATION, walletFingerprint=process.env.FINGERPRINT; try { const identity=await initializePilotCustodyIdentity(owner,process.env.CUSTODIAN,now-100); const intent={requestId:randomUUID(),custodianId:identity.custodianId,marketNetwork:identity.marketNetwork, marketChainId:CHAIN_ID,moneroNetwork:'mainnet',beneficiary:'a'.repeat(64),walletGenerationId,walletFingerprint, accountIndex:0,requestDigest:'b'.repeat(64)}; const allocation=(await reservePilotMoneroDepositIndex(owner,identity,intent,now-50)).reservation; await owner.query("UPDATE wk_custody.pilot_monero_deposit_reservations SET status='creating',create_started_at=$2 WHERE allocation_id=$1", [allocation.allocationId,now-40]); await owner.query("UPDATE wk_custody.pilot_monero_deposit_reservations SET status='registered',monero_address=$2,registered_at=$3 WHERE allocation_id=$1", [allocation.allocationId,process.env.ALLOCATED_ADDRESS,now-30]); const assigned={observationId:'d'.repeat(64)+':'+'e'.repeat(64),transactionId:'d'.repeat(64),address:process.env.ALLOCATED_ADDRESS, amountAtomicXmr:'9007199254740993',height:100,confirmations:11,timestamp:now-1000,unlockTime:'0',accountIndex:0,addressIndex:0, outputPublicKey:'e'.repeat(64),globalOutputIndex:50,locked:false,doubleSpendSeen:false}; const unallocated={...assigned,observationId:'f'.repeat(64)+':'+'1'.repeat(64),transactionId:'f'.repeat(64), address:process.env.SUSPENSE_ADDRESS,amountAtomicXmr:'7',height:101,confirmations:10,addressIndex:1, outputPublicKey:'1'.repeat(64),globalOutputIndex:51}; const persist=(scanId,expectedScanHeight,fromHeight,throughHeight,walletHeight,incoming)=>persistPilotMoneroObservationScan(observer, {scanId,walletGenerationId,walletFingerprint,expectedScanHeight,scannedAt:now+walletHeight, page:{network:'mainnet',walletHeight,fromHeight,throughHeight,primaryAddress:process.env.PRIMARY_ADDRESS,incoming}}); const firstId=randomUUID(),first=await persist(firstId,0,0,110,110,[assigned,unallocated]); const replay=await persist(firstId,0,0,110,110,[assigned,unallocated]); const missing=await persist(randomUUID(),110,100,120,120,[]); const reappeared=await persist(randomUUID(),120,100,130,130,[{...assigned,confirmations:31}]); const rows=await observer.query('SELECT classification,currently_present,amount_atomic_xmr::text AS amount FROM wk_custody.pilot_monero_observations ORDER BY classification'); const events=await observer.query('SELECT event_type,count(*)::int AS n FROM wk_custody.pilot_monero_observation_events GROUP BY event_type ORDER BY event_type'); const journals=await owner.query('SELECT count(*)::int AS n FROM wk_custody.journals'); const credits=await owner.query('SELECT count(*)::int AS n FROM wk_custody.monero_deposit_credit_events'); console.log(JSON.stringify({first,replay,missing,reappeared,rows:rows.rows,events:events.rows,journals:journals.rows[0].n,credits:credits.rows[0].n})); } finally { await observer.end(); await owner.end(); }`; try { await migrateCustody(owner); await admin.query(`CREATE ROLE "${role}" LOGIN`); await grantPilotMoneroObserverRuntime(owner, role); const result = JSON.parse((await run(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', code], { env: { ...process.env, WIKIDECK_NETWORK: 'pilot', WIKIDECK_PILOT_MODE: 'private-mock-only', DB: database, OBSERVER_ROLE: role, CUSTODIAN: custodianId, GENERATION: generation, FINGERPRINT: fingerprint, NOW: String(now), ALLOCATED_ADDRESS: allocatedAddress, SUSPENSE_ADDRESS: suspenseAddress, PRIMARY_ADDRESS: ADDITIONAL_MARKET_FEE.address }, })).stdout.trim()); assert.equal(result.first.observed, 2); assert.equal(result.first.assigned, 1); assert.equal(result.first.unallocated, 1); assert.equal(result.replay.replayed, true); assert.equal(result.missing.notSeen, 2); assert.equal(result.reappeared.reappeared, 1); assert.deepEqual(result.rows.map((row: { classification: string; currently_present: boolean; amount: string }) => [row.classification,row.currently_present,row.amount]), [['assigned',true,'9007199254740993'],['unallocated',false,'7']]); assert.deepEqual(result.events.map((row: { event_type: string; n: number }) => [row.event_type,row.n]), [['not-seen',2],['reappeared',1],['seen',2]]); assert.equal(result.journals, 0); assert.equal(result.credits, 0); assert.equal((await owner.query("SELECT has_table_privilege($1,'wk_custody.accounts','SELECT') AS allowed", [role])).rows[0].allowed, false); const observer = new Pool({ ...config, database, user: role }); try { await assert.rejects(observer.query('DELETE FROM wk_custody.pilot_monero_observation_events')); await assert.rejects(observer.query("UPDATE wk_custody.pilot_monero_observations SET monero_address=$1", [suspenseAddress])); await assert.rejects(observer.query('SELECT * FROM wk_custody.journals')); } finally { await observer.end(); } } finally { await owner.end(); await admin.query(`DROP DATABASE IF EXISTS "${database}" WITH (FORCE)`); await admin.query(`DROP ROLE IF EXISTS "${role}"`); await admin.end(); } }); test('pilot observations classify reserved-wallet outputs, persist replay/reorg evidence, and never post credits', async t => { const admin = new Pool(config), database = `pilot_observations_${randomUUID().replaceAll('-', '')}`; const role = `pilot_observer_${randomUUID().replaceAll('-', '')}`; await admin.query(`CREATE DATABASE "${database}"`); const owner = new Pool({ ...config, database }), run = promisify(execFile); const custodianId = randomUUID(), generation = randomUUID(), fingerprint = 'c'.repeat(64), now = 1_800_000_000_000; const allocatedAddress = await syntheticSubaddress(21), suspenseAddress = await syntheticSubaddress(22); const primaryAddress = ADDITIONAL_MARKET_FEE.address; const code = `import { Pool } from 'pg'; import { randomUUID } from 'node:crypto'; import { CHAIN_ID } from './server/testnet/consensus.ts'; import { initializePilotCustodyIdentity } from './server/market/pilot-custody-identity.ts'; import { reservePilotMoneroDepositIndex } from './server/market/pilot-monero-deposit-allocation.ts'; import { persistPilotMoneroObservationScan } from './server/market/pilot-monero-observation-store.ts'; const config={host:'127.0.0.1',database:process.env.DB,user:'postgres',ssl:false,max:4}; const owner=new Pool(config), observer=new Pool({...config,user:process.env.OBSERVER_ROLE}); const now=Number(process.env.NOW), walletGenerationId=process.env.GENERATION, walletFingerprint=process.env.FINGERPRINT; try { const identity=await initializePilotCustodyIdentity(owner,process.env.CUSTODIAN,now-100); const intent={requestId:randomUUID(),custodianId:identity.custodianId,marketNetwork:identity.marketNetwork, marketChainId:CHAIN_ID,moneroNetwork:'mainnet',beneficiary:'a'.repeat(64),walletGenerationId,walletFingerprint, accountIndex:0,requestDigest:'b'.repeat(64)}; const allocation=(await reservePilotMoneroDepositIndex(owner,identity,intent,now-50)).reservation; await owner.query("UPDATE wk_custody.pilot_monero_deposit_reservations SET status='creating',create_started_at=$2 WHERE allocation_id=$1", [allocation.allocationId,now-40]); await owner.query("UPDATE wk_custody.pilot_monero_deposit_reservations SET status='registered',monero_address=$2,registered_at=$3 WHERE allocation_id=$1", [allocation.allocationId,process.env.ALLOCATED_ADDRESS,now-30]); const assigned={observationId:'d'.repeat(64)+':'+'e'.repeat(64),transactionId:'d'.repeat(64),address:process.env.ALLOCATED_ADDRESS, amountAtomicXmr:'9007199254740993',height:100,confirmations:11,timestamp:now-1000,unlockTime:'0',accountIndex:0,addressIndex:0, outputPublicKey:'e'.repeat(64),globalOutputIndex:50,locked:false,doubleSpendSeen:false}; const unallocated={...assigned,observationId:'f'.repeat(64)+':'+'1'.repeat(64),transactionId:'f'.repeat(64), address:process.env.SUSPENSE_ADDRESS,amountAtomicXmr:'7',height:101,confirmations:10,addressIndex:1, outputPublicKey:'1'.repeat(64),globalOutputIndex:51}; const persist=(scanId,expectedScanHeight,fromHeight,throughHeight,walletHeight,incoming)=>persistPilotMoneroObservationScan(observer, {scanId,walletGenerationId,walletFingerprint,expectedScanHeight,scannedAt:now+walletHeight, page:{network:'mainnet',walletHeight,fromHeight,throughHeight,primaryAddress:process.env.PRIMARY_ADDRESS,incoming}}); const firstId=randomUUID(),first=await persist(firstId,0,0,110,110,[assigned,unallocated]); const replay=await persist(firstId,0,0,110,110,[assigned,unallocated]); const missing=await persist(randomUUID(),110,100,120,120,[]); const reappeared=await persist(randomUUID(),120,100,130,130,[{...assigned,confirmations:31}]); const rows=await observer.query('SELECT classification,currently_present,amount_atomic_xmr::text AS amount FROM wk_custody.pilot_monero_observations ORDER BY classification'); const events=await observer.query('SELECT event_type,count(*)::int AS n FROM wk_custody.pilot_monero_observation_events GROUP BY event_type ORDER BY event_type'); const journals=await owner.query('SELECT count(*)::int AS n FROM wk_custody.journals'); const credits=await owner.query('SELECT count(*)::int AS n FROM wk_custody.monero_deposit_credit_events'); console.log(JSON.stringify({first,replay,missing,reappeared,rows:rows.rows,events:events.rows,journals:journals.rows[0].n,credits:credits.rows[0].n})); } finally { await observer.end(); await owner.end(); }`; try { await migrateCustody(owner); await admin.query(`CREATE ROLE "${role}" LOGIN`); await grantPilotMoneroObserverRuntime(owner, role); const result = JSON.parse((await run(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', code], { env: { ...process.env, WIKIDECK_NETWORK: 'pilot', WIKIDECK_PILOT_MODE: 'private-mock-only', DB: database, OBSERVER_ROLE: role, CUSTODIAN: custodianId, GENERATION: generation, FINGERPRINT: fingerprint, NOW: String(now), ALLOCATED_ADDRESS: allocatedAddress, SUSPENSE_ADDRESS: suspenseAddress, PRIMARY_ADDRESS: primaryAddress }, })).stdout.trim()); assert.equal(result.first.observed, 2); assert.equal(result.first.assigned, 1); assert.equal(result.first.unallocated, 1); assert.equal(result.replay.replayed, true); assert.equal(result.missing.notSeen, 2); assert.equal(result.reappeared.reappeared, 1); assert.deepEqual(result.rows.map((row: { classification: string; currently_present: boolean; amount: string }) => [row.classification,row.currently_present,row.amount]), [['assigned',true,'9007199254740993'],['unallocated',false,'7']]); assert.deepEqual(result.events.map((row: { event_type: string; n: number }) => [row.event_type,row.n]), [['not-seen',2],['reappeared',1],['seen',2]]); assert.equal(result.journals, 0); assert.equal(result.credits, 0); assert.equal((await owner.query("SELECT has_table_privilege($1,'wk_custody.accounts','SELECT') AS allowed", [role])).rows[0].allowed, false); const observer = new Pool({ ...config, database, user: role }); try { await assert.rejects(observer.query('DELETE FROM wk_custody.pilot_monero_observation_events')); await assert.rejects(observer.query("UPDATE wk_custody.pilot_monero_observations SET monero_address=$1", [suspenseAddress])); await assert.rejects(observer.query('SELECT * FROM wk_custody.journals')); } finally { await observer.end(); } } finally { await owner.end(); await admin.query(`DROP DATABASE IF EXISTS "${database}" WITH (FORCE)`); await admin.query(`DROP ROLE IF EXISTS "${role}"`); await admin.end(); } }); test('pilot review preparation is read-only, identity-bound, and fails closed on ineligible evidence', async t => { const admin = new Pool(config), database = `pilot_review_${randomUUID().replaceAll('-', '')}`; const role = `pilot_review_${randomUUID().replaceAll('-', '')}`; await admin.query(`CREATE DATABASE "${database}"`); const owner = new Pool({ ...config, database }), run = promisify(execFile); const custodianId = randomUUID(), generation = randomUUID(), fingerprint = 'c'.repeat(64), now = Date.now(); const firstAddress = await syntheticSubaddress(31), secondAddress = await syntheticSubaddress(32); const suspenseAddress = await syntheticSubaddress(33); const code = `import { Pool } from 'pg'; import { randomUUID } from 'node:crypto'; import { CHAIN_ID } from './server/testnet/consensus.ts'; import { initializePilotCustodyIdentity } from './server/market/pilot-custody-identity.ts'; import { reservePilotMoneroDepositIndex } from './server/market/pilot-monero-deposit-allocation.ts'; import { persistPilotMoneroObservationScan } from './server/market/pilot-monero-observation-store.ts'; import { preparePilotMoneroDepositReview } from './server/market/pilot-monero-deposit-review.ts'; const config={host:'127.0.0.1',database:process.env.DB,user:'postgres',ssl:false,max:4}; const owner=new Pool(config), review=new Pool({...config,user:process.env.REVIEW_ROLE}); const now=Number(process.env.NOW), generation=process.env.GENERATION, fingerprint=process.env.FINGERPRINT; const policy={minimumConfirmations:10,maximumObservationAgeMs:30000}; const failed=async (generationId,observationId,at=now+300,selectedPolicy=policy)=>{ try { await preparePilotMoneroDepositReview(review,{walletGenerationId:generationId,observationId},selectedPolicy,at); return false; } catch { return true; } }; try { const identity=await initializePilotCustodyIdentity(owner,process.env.CUSTODIAN,now-100); const reserve=async (beneficiary,requestDigest)=>{ const intent={requestId:randomUUID(),custodianId:identity.custodianId,marketNetwork:identity.marketNetwork, marketChainId:CHAIN_ID,moneroNetwork:'mainnet',beneficiary,walletGenerationId:generation,walletFingerprint:fingerprint, accountIndex:0,requestDigest}; return (await reservePilotMoneroDepositIndex(owner,identity,intent,now-50)).reservation; }; const first=await reserve('a'.repeat(64),'b'.repeat(64)); const second=await reserve('c'.repeat(64),'d'.repeat(64)); for (const [reservation,address] of [[first,process.env.FIRST_ADDRESS],[second,process.env.SECOND_ADDRESS]]) { await owner.query("UPDATE wk_custody.pilot_monero_deposit_reservations SET status='creating',create_started_at=$2 WHERE allocation_id=$1", [reservation.allocationId,now-40]); await owner.query("UPDATE wk_custody.pilot_monero_deposit_reservations SET status='registered',monero_address=$2,registered_at=$3 WHERE allocation_id=$1", [reservation.allocationId,address,now-30]); } const assigned={observationId:'e'.repeat(64)+':'+'f'.repeat(64),transactionId:'e'.repeat(64),address:process.env.FIRST_ADDRESS, amountAtomicXmr:'9007199254740993',height:100,confirmations:11,timestamp:Math.floor(now/1000)-100, unlockTime:'0',accountIndex:0,addressIndex:0,outputPublicKey:'f'.repeat(64),globalOutputIndex:50, locked:false,doubleSpendSeen:false}; const futureUnlock={...assigned,observationId:'1'.repeat(64)+':'+'2'.repeat(64),transactionId:'1'.repeat(64), address:process.env.SECOND_ADDRESS,height:101,confirmations:10,unlockTime:String(Math.floor((now+60000)/1000)), addressIndex:1,outputPublicKey:'2'.repeat(64),globalOutputIndex:51}; const suspense={...assigned,observationId:'3'.repeat(64)+':'+'4'.repeat(64),transactionId:'3'.repeat(64), address:process.env.SUSPENSE_ADDRESS,amountAtomicXmr:'7',height:102,confirmations:9,unlockTime:'0',addressIndex:2, outputPublicKey:'4'.repeat(64),globalOutputIndex:52}; const persist=(scanId,expectedScanHeight,fromHeight,throughHeight,walletHeight,incoming,scannedAt)=>persistPilotMoneroObservationScan(owner, {scanId,walletGenerationId:generation,walletFingerprint:fingerprint,expectedScanHeight,scannedAt, page:{network:'mainnet',walletHeight,fromHeight,throughHeight,primaryAddress:process.env.PRIMARY_ADDRESS,incoming}}); await persist(randomUUID(),0,0,110,110,[assigned,futureUnlock,suspense],now+100); const subject=await preparePilotMoneroDepositReview(review,{walletGenerationId:generation,observationId:assigned.observationId},policy,now+300); const stricterFloorRejected=await failed(generation,assigned.observationId,now+300,{...policy,minimumConfirmations:12}); const futureUnlockRejected=await failed(generation,futureUnlock.observationId); const unallocatedRejected=await failed(generation,suspense.observationId); const staleRejected=await failed(generation,assigned.observationId,now+40000); const before=await owner.query('SELECT (SELECT count(*)::int FROM wk_custody.journals) AS journals, '+ '(SELECT count(*)::int FROM wk_custody.entries) AS entries, '+ '(SELECT count(*)::int FROM wk_custody.accounts) AS accounts, '+ '(SELECT count(*)::int FROM wk_custody.monero_deposit_credit_events) AS credits'); const changed={...assigned,amountAtomicXmr:'9007199254740994',confirmations:21}; await persist(randomUUID(),110,100,120,120,[changed,{...futureUnlock,confirmations:20}],now+200); const changedHistoryRejected=await failed(generation,assigned.observationId,now+300); let updateDenied=false, financialReadDenied=false; try { await review.query("UPDATE wk_custody.pilot_monero_observations SET locked=true"); } catch { updateDenied=true; } try { await review.query('SELECT * FROM wk_custody.journals'); } catch { financialReadDenied=true; } console.log(JSON.stringify({subject,stricterFloorRejected,futureUnlockRejected,unallocatedRejected,staleRejected, changedHistoryRejected,before:before.rows[0],updateDenied,financialReadDenied})); } finally { await review.end(); await owner.end(); }`; try { await migrateCustody(owner); await admin.query(`CREATE ROLE "${role}" LOGIN`); await grantPilotMoneroDepositReviewRuntime(owner, role); const privileges = await owner.query(`SELECT has_table_privilege($1,'wk_custody.pilot_monero_observations','SELECT') AS read_observations, has_table_privilege($1,'wk_custody.pilot_monero_observations','UPDATE') AS write_observations, has_table_privilege($1,'wk_custody.pilot_monero_observation_events','INSERT') AS append_events, has_table_privilege($1,'wk_custody.accounts','SELECT') AS read_accounts, has_table_privilege($1,'wk_custody.journals','SELECT') AS read_journals`, [role]); assert.deepEqual(Object.values(privileges.rows[0]), [true, false, false, false, false]); const result = JSON.parse((await run(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', code], { env: { ...process.env, WIKIDECK_NETWORK: 'pilot', WIKIDECK_PILOT_MODE: 'private-mock-only', DB: database, REVIEW_ROLE: role, CUSTODIAN: custodianId, GENERATION: generation, FINGERPRINT: fingerprint, NOW: String(now), FIRST_ADDRESS: firstAddress, SECOND_ADDRESS: secondAddress, SUSPENSE_ADDRESS: suspenseAddress, PRIMARY_ADDRESS: ADDITIONAL_MARKET_FEE.address }, })).stdout.trim()); assert.equal(result.subject.policyVersion, 'pilot-monero-deposit-review-v1'); assert.equal(result.subject.preparationOnly, true); assert.equal(result.subject.crediting, 'disabled'); assert.equal(result.subject.marketNetwork, 'wikideck-randomx-pilot-1'); assert.equal(result.subject.moneroNetwork, 'mainnet'); assert.equal(result.subject.amountAtomicXmr, '9007199254740993'); assert.equal(result.subject.confirmations, 11); assert.equal(result.subject.minimumConfirmations, 10); assert.equal(result.subject.eventHistoryCount, 1); assert.match(result.subject.subjectDigest, /^[a-f0-9]{64}$/); assert.equal(result.stricterFloorRejected, true); assert.equal(result.futureUnlockRejected, true); assert.equal(result.unallocatedRejected, true); assert.equal(result.staleRejected, true); assert.equal(result.changedHistoryRejected, true); assert.equal(result.updateDenied, true); assert.equal(result.financialReadDenied, true); assert.deepEqual(result.before, { journals: 0, entries: 0, accounts: 0, credits: 0 }); } finally { await owner.end(); await admin.query(`DROP DATABASE IF EXISTS "${database}" WITH (FORCE)`); await admin.query(`DROP ROLE IF EXISTS "${role}"`); await admin.end(); } }); test('mainnet custody identity bootstrap is one-time, chain-bound, and does not open withdrawals', async t => { assert.equal(parseMainnetCustodianId('AAAAAAAA-AAAA-4AAA-8AAA-AAAAAAAAAAAA'), 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa'); assert.throws(() => parseMainnetCustodianId('not-a-uuid'), /Invalid mainnet custodian UUID/); const admin = new Pool(config), database = `mainnet_identity_${randomUUID().replaceAll('-', '')}`; await admin.query(`CREATE DATABASE "${database}"`); const target = new Pool({ ...config, database }); const run = promisify(execFile), custodianId = randomUUID(); try { await migrateCustody(target); const code = `import { Pool } from 'pg'; import { initializeMainnetCustodyIdentity } from './server/market/mainnet-custody-identity.ts'; const pool = new Pool({host:'127.0.0.1',database:process.env.TEST_DATABASE,user:'postgres',ssl:false}); try { console.log(JSON.stringify(await initializeMainnetCustodyIdentity(pool, process.env.TEST_CUSTODIAN_ID))); } finally { await pool.end(); }`; const env = { ...process.env, WIKIDECK_NETWORK: 'mainnet', TEST_DATABASE: database, TEST_CUSTODIAN_ID: custodianId }; const first = JSON.parse((await run(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', code], { env })).stdout.trim()); const replay = JSON.parse((await run(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', code], { env })).stdout.trim()); assert.equal(first.mode, 'mainnet-custody'); assert.equal(first.network, 'wikideck-randomx-mainnet-1'); assert.equal(first.id, custodianId); assert.deepEqual(replay, first); await assert.rejects(run(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', code], { env: { ...env, TEST_CUSTODIAN_ID: randomUUID() }, })); assert.equal((await target.query('SELECT count(*)::int AS n FROM wk_custody.journals')).rows[0].n, 0); } finally { await target.end(); await admin.query(`DROP DATABASE IF EXISTS "${database}" WITH (FORCE)`); await admin.end(); } }); test('customer-signed mainnet withdrawal reserves available balance and can cancel before signer handoff', async t => { const admin = new Pool(config), database = `mainnet_withdraw_${randomUUID().replaceAll('-', '')}`; await admin.query(`CREATE DATABASE "${database}"`); const owner = new Pool({ ...config, database }), custodianId = randomUUID(), run = promisify(execFile); const now = Date.now(), withdrawalRole = 'monero_withdrawal_test', observerRole = 'monero_observer', creditRole = 'monero_deposit_credit'; await admin.query(`DO $$ BEGIN IF NOT EXISTS(SELECT 1 FROM pg_roles WHERE rolname='${withdrawalRole}') THEN CREATE ROLE ${withdrawalRole} LOGIN; END IF; IF NOT EXISTS(SELECT 1 FROM pg_roles WHERE rolname='${observerRole}') THEN CREATE ROLE ${observerRole} LOGIN; END IF; IF NOT EXISTS(SELECT 1 FROM pg_roles WHERE rolname='${creditRole}') THEN CREATE ROLE ${creditRole} LOGIN; END IF; END $$`); const runtime = new Pool({ ...config, database, user: withdrawalRole }); const observer = new Pool({ ...config, database, user: observerRole }); const creditRuntime = new Pool({ ...config, database, user: creditRole }); try { await migrateCustody(owner); const bootstrap = `import { Pool } from 'pg'; import { initializeMainnetCustodyIdentity } from './server/market/mainnet-custody-identity.ts'; const p=new Pool({host:'127.0.0.1',database:process.env.DB,user:'postgres',ssl:false}); try { await initializeMainnetCustodyIdentity(p,process.env.CUSTODIAN); } finally { await p.end(); }`; await run(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', bootstrap], { env: { ...process.env, WIKIDECK_NETWORK: 'mainnet', DB: database, CUSTODIAN: custodianId }, }); const mainnetChain = JSON.parse((await owner.query("SELECT value FROM wk_custody.custody_meta WHERE key='identity'")).rows[0].value).chainId as string; await grantMoneroObserverRuntime(owner, observerRole); await grantMoneroDepositCreditRuntime(owner, creditRole); await grantMoneroWithdrawalRuntime(owner, withdrawalRole); assert.equal((await owner.query("SELECT has_table_privilege($1,'wk_custody.accounts','SELECT') AS allowed", [withdrawalRole])).rows[0].allowed, true); assert.equal((await owner.query("SELECT has_table_privilege($1,'wk_custody.withdrawals','SELECT') AS allowed", [withdrawalRole])).rows[0].allowed, false); assert.equal((await owner.query("SELECT has_table_privilege($1,'wk_custody.monero_transfer_observations','SELECT') AS allowed", [withdrawalRole])).rows[0].allowed, false); const walletFingerprint = 'd'.repeat(64), generation = randomUUID(), allocationRequest = randomUUID(); const depositAddress = await syntheticSubaddress(11), outputTx = 'e'.repeat(64), outputKey = 'f'.repeat(64); const allocation = await reserveMoneroDepositAllocation(owner, { requestId: allocationRequest, custodianId, marketNetwork: 'wikideck-randomx-mainnet-1', marketChainId: mainnetChain, beneficiary: buyer.actor, walletGenerationId: generation, walletFingerprint, accountIndex: 0, reservedAt: now - 1 }); await registerMoneroDepositSubaddress(owner, { allocationId: allocation.allocation.allocationId, walletGenerationId: generation, moneroAddress: depositAddress, registeredAt: now }); const observationId = `${outputTx}:${outputKey}`; const observation = { observationId, transactionId: outputTx, amountAtomicXmr: '1000', height: 190, address: depositAddress, confirmations: 11, timestamp: now, unlockTime: '0', accountIndex: 0, addressIndex: allocation.allocation.addressIndex, outputPublicKey: outputKey, globalOutputIndex: 90, locked: false, doubleSpendSeen: false }; const page = { network: 'mainnet' as const, walletHeight: 200, fromHeight: 0, throughHeight: 200, primaryAddress: ADDITIONAL_MARKET_FEE.address, incoming: [observation] }; await persistMoneroObservationScan(observer, { scanId: randomUUID(), walletFingerprint, expectedScanHeight: 0, scannedAt: now, page }); const policy: DepositCreditPolicy = { approvedReviewerOwners: [host.actor], minimumConfirmations: 10, maximumObservationAgeMs: 120_000 }; const creditSubject = await prepareDepositCreditReview(creditRuntime, walletFingerprint, observationId, policy, now); const creditNonce = randomUUID(), creditExpiry = now + 60_000; const creditApproval = { subject: creditSubject, reviewerOwner: host.actor, reviewerPublicKey: host.publicKey, nonce: creditNonce, expiresAt: creditExpiry, signature: host.sign(depositCreditApprovalMessage(creditSubject, host.actor, creditNonce, creditExpiry)) }; const pendingCredit = await postReviewedDepositPendingCredit(creditRuntime, creditApproval, policy, now); const releaseSubject = await prepareDepositCreditReleaseReview(creditRuntime, pendingCredit.creditId, policy, now); const releaseNonce = randomUUID(), releaseExpiry = now + 60_000; const releaseApproval = { subject: releaseSubject, reviewerOwner: host.actor, reviewerPublicKey: host.publicKey, nonce: releaseNonce, expiresAt: releaseExpiry, signature: host.sign(depositCreditReleaseApprovalMessage(releaseSubject, host.actor, releaseNonce, releaseExpiry)) }; await releaseReviewedDepositCredit(creditRuntime, releaseApproval, policy, now); assert.equal((await owner.query('SELECT balance::text FROM wk_custody.accounts WHERE id=$1', [`customer:${buyer.actor}:available`])).rows[0].balance, '1000', 'mainnet reviewed deposit release funds customer available balance'); const balanceUnsigned: MainnetCustodyBalanceRequestUnsigned = { version: 1, network: 'wikideck-randomx-mainnet-1', chainId: mainnetChain, custodianId, beneficiary: buyer.actor, publicKey: buyer.publicKey, nonce: randomUUID(), expiresAt: now + 60_000 }; const balanceRequest = { ...balanceUnsigned, signature: buyer.sign(mainnetCustodyBalanceMessage(balanceUnsigned)) }; assert.deepEqual(await readMainnetCustodyBalance(runtime, balanceRequest, now), { availableAtomicXmr: '1000', depositPendingAtomicXmr: '0', withdrawalReservedAtomicXmr: '0', }); await assert.rejects(readMainnetCustodyBalance(runtime, { ...balanceRequest, beneficiary: seller.actor }, now), /signature/); // Model an accidental/operator-created liability balance above reviewed on-chain credits. const fixtureClient = await owner.connect(); try { await fixtureClient.query('BEGIN'); await fixtureClient.query("INSERT INTO wk_custody.accounts(id,normal) VALUES('treasury:fixture-extra', 'debit') ON CONFLICT DO NOTHING"); await fixtureClient.query("INSERT INTO wk_custody.journals(id,kind,created) VALUES('fixture-unbacked-extra','fixture-unbacked-extra',$1)", [now]); await fixtureClient.query("INSERT INTO wk_custody.entries VALUES('fixture-unbacked-extra',0,'treasury:fixture-extra',500,0),('fixture-unbacked-extra',1,$1,0,500)", [`customer:${buyer.actor}:available`]); await fixtureClient.query('COMMIT'); } catch (error) { await fixtureClient.query('ROLLBACK').catch(() => {}); throw error; } finally { fixtureClient.release(); } const requestId = randomUUID(); const unsigned: MainnetWithdrawalRequestUnsigned = { version: 1, network: 'wikideck-randomx-mainnet-1', chainId: mainnetChain, custodianId, beneficiary: buyer.actor, publicKey: buyer.publicKey, requestId, destination: ADDITIONAL_MARKET_FEE.address, amountAtomicXmr: '700', maxFeeAtomicXmr: '50', expiresAt: now + 60_000 }; const request = { ...unsigned, signature: buyer.sign(mainnetWithdrawalMessage(unsigned)) }; const invalidDestinationUnsigned = { ...unsigned, requestId: randomUUID(), destination: '1'.repeat(95) }; await assert.rejects(reserveMainnetWithdrawal(runtime, { ...invalidDestinationUnsigned, signature: buyer.sign(mainnetWithdrawalMessage(invalidDestinationUnsigned)) }, now), /address/); const overBackingUnsigned = { ...unsigned, requestId: randomUUID(), amountAtomicXmr: '1100', maxFeeAtomicXmr: '0' }; await assert.rejects(reserveMainnetWithdrawal(runtime, { ...overBackingUnsigned, signature: buyer.sign(mainnetWithdrawalMessage(overBackingUnsigned)) }, now), /exceed reviewed released deposit backing/); const first = await reserveMainnetWithdrawal(runtime, request, now); assert.equal(first.status, 'reserved'); assert.equal(first.amountAtomicXmr, '700'); const replay = await reserveMainnetWithdrawal(runtime, request, now); assert.equal(replay.requestId, requestId); assert.equal(replay.status, 'reserved'); assert.equal((await reserveMainnetWithdrawal(runtime, request, now + 120_000)).status, 'reserved', 'replay of a committed request remains idempotent after its authorization expires'); assert.equal((await owner.query('SELECT balance::text FROM wk_custody.accounts WHERE id=$1', [`customer:${buyer.actor}:available`])).rows[0].balance, '750'); assert.equal((await owner.query('SELECT balance::text FROM wk_custody.accounts WHERE id=$1', [`customer:${buyer.actor}:monero-withdrawal:${requestId}`])).rows[0].balance, '750'); const afterReserveUnsigned = { ...balanceUnsigned, nonce: randomUUID() }; const afterReserve = { ...afterReserveUnsigned, signature: buyer.sign(mainnetCustodyBalanceMessage(afterReserveUnsigned)) }; assert.deepEqual(await readMainnetCustodyBalance(runtime, afterReserve, now), { availableAtomicXmr: '750', depositPendingAtomicXmr: '0', withdrawalReservedAtomicXmr: '750', }); await assert.rejects(runtime.query('SELECT * FROM wk_custody.journals'), /permission denied/); await assert.rejects(reserveMainnetWithdrawal(runtime, { ...request, beneficiary: seller.actor }, now), /signature/); const cancelUnsigned: MainnetWithdrawalCancellationUnsigned = { version: 1, network: 'wikideck-randomx-mainnet-1', chainId: mainnetChain, custodianId, beneficiary: buyer.actor, publicKey: buyer.publicKey, cancellationId: randomUUID(), withdrawalRequestId: requestId, expiresAt: now + 60_000 }; const cancel = { ...cancelUnsigned, signature: buyer.sign(mainnetWithdrawalCancellationMessage(cancelUnsigned)) }; const cancelled = await cancelMainnetWithdrawal(runtime, cancel, now); assert.equal(cancelled.status, 'cancelled'); assert.equal((await cancelMainnetWithdrawal(runtime, cancel, now + 120_000)).status, 'cancelled', 'replay of an acknowledged cancellation remains idempotent after expiry'); assert.equal((await owner.query('SELECT balance::text FROM wk_custody.accounts WHERE id=$1', [`customer:${buyer.actor}:available`])).rows[0].balance, '1500'); assert.equal((await owner.query('SELECT balance::text FROM wk_custody.accounts WHERE id=$1', [`customer:${buyer.actor}:monero-withdrawal:${requestId}`])).rows[0].balance, '0'); const audit = await owner.connect(); try { await audit.query('BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY'); await auditBusinessLiabilities(audit); await audit.query('COMMIT'); } catch (error) { await audit.query('ROLLBACK').catch(() => {}); throw error; } finally { audit.release(); } const alternateCancelUnsigned = { ...cancelUnsigned, cancellationId: randomUUID() }; const alternateCancel = { ...alternateCancelUnsigned, signature: buyer.sign(mainnetWithdrawalCancellationMessage(alternateCancelUnsigned)) }; await assert.rejects(cancelMainnetWithdrawal(runtime, alternateCancel, now), /different signed terms/); } finally { await creditRuntime.end(); await observer.end(); await runtime.end(); await owner.end(); await admin.query(`DROP DATABASE IF EXISTS "${database}" WITH (FORCE)`); await admin.end(); } }); test('deposit allocation reserves unique beneficiary-bound indexes before RPC and only registers an immutable subaddress', async t => { const f = await fixture(t); await f.admin.query("DO $$ BEGIN IF NOT EXISTS(SELECT 1 FROM pg_roles WHERE rolname='monero_allocation_observer') THEN CREATE ROLE monero_allocation_observer LOGIN; END IF; END $$"); await grantMoneroObserverRuntime(f.admin, 'monero_allocation_observer'); const observer = new Pool({ ...config, user: 'monero_allocation_observer' }); const custodyRuntime = new Pool(f.runtime); const secondAllocatorProcess = new Pool(config); const generation = randomUUID(), wallet = 'f'.repeat(64), reservedAt = 1_800_000_000; const input = (requestId: string, beneficiary: string) => ({ requestId, custodianId: f.market.id, marketNetwork: NETWORK, marketChainId: CHAIN_ID, beneficiary, walletGenerationId: generation, walletFingerprint: wallet, accountIndex: 0, reservedAt }); try { const firstRequest = randomUUID(); const firstInput = input(firstRequest, buyer.actor); const first = await reserveMoneroDepositAllocation(f.admin, firstInput); assert.equal(first.allocation.addressIndex, 0); assert.equal(first.allocation.status, 'reserved'); assert.equal(first.allocation.moneroAddress, null, 'reservation is committed before any address is available'); assert.equal(first.replayed, false); const replay = await reserveMoneroDepositAllocation(f.admin, firstInput); assert.equal(replay.replayed, true); assert.deepEqual(replay.allocation, first.allocation); assert.deepEqual((await reserveMoneroDepositAllocation(f.admin, input(randomUUID(), buyer.actor))).allocation, first.allocation, 'beneficiary receives the same reserved allocation rather than a new index'); await assert.rejects(reserveMoneroDepositAllocation(f.admin, input(firstRequest, seller.actor)), /reused with different terms/); await assert.rejects(reserveMoneroDepositAllocation(f.admin, { ...firstInput, accountIndex: 1 }), /reused with different terms/); const concurrent = await Promise.all([ reserveMoneroDepositAllocation(f.admin, input(randomUUID(), other.actor)), reserveMoneroDepositAllocation(secondAllocatorProcess, input(randomUUID(), seller.actor)), ]); assert.deepEqual(concurrent.map(item => item.allocation.addressIndex).sort((a, b) => a - b), [1, 2]); const subaddress = await syntheticSubaddress(); await assert.rejects(registerMoneroDepositSubaddress(f.admin, { allocationId: concurrent[0].allocation.allocationId, walletGenerationId: generation, moneroAddress: await syntheticFeeAddress(), registeredAt: reservedAt + 1 }), /Expected a Monero mainnet subaddress/); const registered = await registerMoneroDepositSubaddress(f.admin, { allocationId: first.allocation.allocationId, walletGenerationId: generation, moneroAddress: subaddress, registeredAt: reservedAt + 1 }); assert.equal(registered.allocation.status, 'registered'); assert.equal(registered.allocation.moneroAddress, subaddress); assert.equal((await registerMoneroDepositSubaddress(f.admin, { allocationId: first.allocation.allocationId, walletGenerationId: generation, moneroAddress: subaddress, registeredAt: reservedAt + 2 })).replayed, true); await assert.rejects(registerMoneroDepositSubaddress(f.admin, { allocationId: first.allocation.allocationId, walletGenerationId: generation, moneroAddress: await syntheticSubaddress(4), registeredAt: reservedAt + 3 }), /cannot be replaced/); await assert.rejects(registerMoneroDepositSubaddress(f.admin, { allocationId: concurrent[0].allocation.allocationId, walletGenerationId: generation, moneroAddress: subaddress, registeredAt: reservedAt + 4 }), /already registered/); await assert.rejects(custodyRuntime.query('SELECT * FROM wk_custody.monero_deposit_allocations'), /permission denied/); await assert.rejects(observer.query('SELECT * FROM wk_custody.monero_deposit_allocations'), /permission denied/); await assert.rejects(observer.query("INSERT INTO wk_custody.monero_deposit_allocations(allocation_id) VALUES($1)", [randomUUID()]), /permission denied/); await assert.rejects(f.admin.query('DELETE FROM wk_custody.monero_deposit_allocations WHERE allocation_id=$1', [first.allocation.allocationId]), /cannot be deleted/); await assert.rejects(f.admin.query('UPDATE wk_custody.monero_deposit_allocations SET beneficiary=$2 WHERE allocation_id=$1', [first.allocation.allocationId, other.actor]), /identity is immutable/); const recoveryGeneration = randomUUID(); const recoveryFingerprint = await moneroWalletFingerprint(ADDITIONAL_MARKET_FEE.address); const recoveryReservation = await reserveMoneroDepositAllocation(f.admin, { ...input(randomUUID(), buyer.actor), walletGenerationId: recoveryGeneration, walletFingerprint: recoveryFingerprint }); const walletAddresses = new Map(); const recoveryAddress = await syntheticSubaddress(6); let createCalls = 0, addressReads = 0, liveFingerprint = 'b'.repeat(64); const fakeRpc = { async verifyWallet() { return liveFingerprint; }, async getSubaddress(accountIndex: number, addressIndex: number) { addressReads++; return walletAddresses.get(`${accountIndex}:${addressIndex}`) ?? null; }, async createSubaddress(accountIndex: number, label: string) { createCalls++; const row = { address: recoveryAddress, label }; walletAddresses.set(`${accountIndex}:${recoveryReservation.allocation.addressIndex}`, row); if (createCalls === 1) throw new Error('fixture: RPC response lost after wallet persisted address'); return { address: row.address, addressIndex: recoveryReservation.allocation.addressIndex }; }, }; await assert.rejects(createOrRecoverMoneroDepositSubaddress(f.admin, fakeRpc, recoveryReservation.allocation.allocationId, recoveryGeneration, reservedAt + 4), /identity differs/); assert.equal(addressReads, 0); assert.equal(createCalls, 0, 'wallet mismatch must fail before address operations'); liveFingerprint = recoveryFingerprint; await assert.rejects(createOrRecoverMoneroDepositSubaddress(f.admin, fakeRpc, recoveryReservation.allocation.allocationId, recoveryGeneration, reservedAt + 5), /response lost/); const recovered = await createOrRecoverMoneroDepositSubaddress(f.admin, fakeRpc, recoveryReservation.allocation.allocationId, recoveryGeneration, reservedAt + 6); assert.equal(recovered.allocation.status, 'registered'); assert.equal(recovered.allocation.moneroAddress, recoveryAddress); assert.equal(recovered.replayed, false); assert.equal(createCalls, 1, 'retry must recover by deterministic wallet label rather than create a duplicate'); assert.equal((await f.admin.query('SELECT count(*)::int AS n FROM wk_custody.journals')).rows[0].n, 0, 'address allocation must never create financial postings'); } finally { await secondAllocatorProcess.end(); await custodyRuntime.end(); await observer.end(); } }); test('signed mainnet deposit enrollment reserves and recovers one beneficiary address with the allocator role', async t => { const f = await fixture(t); await f.admin.query("DO $$ BEGIN IF NOT EXISTS(SELECT 1 FROM pg_roles WHERE rolname='monero_deposit_allocator_test') THEN CREATE ROLE monero_deposit_allocator_test LOGIN; END IF; END $$"); await grantMoneroDepositAllocatorRuntime(f.admin, 'monero_deposit_allocator_test'); await assert.rejects(grantMoneroDepositAllocatorRuntime(f.admin, 'custody_runtime'), /separate role/); const allocator = new Pool({ ...config, user: 'monero_deposit_allocator_test' }); const walletGenerationId = randomUUID(), walletFingerprint = 'a'.repeat(64), chainId = 'b'.repeat(64); const now = Date.now(), depositAddress = await syntheticSubaddress(8), labels = new Map(); let createCalls = 0, verifyCalls = 0; const rpc = { async verifyWallet() { verifyCalls++; return walletFingerprint; }, async getSubaddress(accountIndex: number, addressIndex: number) { const label = labels.get(`${accountIndex}:${addressIndex}`); return label ? { address: depositAddress, label } : null; }, async createSubaddress(accountIndex: number, label: string) { createCalls++; labels.set(`${accountIndex}:${createCalls - 1}`, label); return { address: depositAddress, addressIndex: createCalls - 1 }; }, }; const context = { custodianId: f.market.id, chainId, walletGenerationId, walletFingerprint, accountIndex: 0, now: () => now }; const signedRequest = (requestId: string) => { const unsigned: MoneroDepositAddressRequestUnsigned = { version: 1, network: 'wikideck-randomx-mainnet-1', chainId, custodianId: f.market.id, actor: buyer.actor, publicKey: buyer.publicKey, requestId, walletGenerationId, walletFingerprint, accountIndex: 0, expiresAt: now + 60_000 }; return { ...unsigned, signature: buyer.sign(moneroDepositAddressMessage(unsigned)) }; }; try { const request = signedRequest(randomUUID()); const created = await enrollMoneroDepositAddress(allocator, rpc, context, request); assert.equal(created.network, 'mainnet'); assert.equal(created.address, depositAddress); assert.equal(created.addressIndex, 0); assert.equal(created.replayed, false); const replay = await enrollMoneroDepositAddress(allocator, rpc, context, request); assert.equal(replay.allocationId, created.allocationId); assert.equal(replay.address, created.address); assert.equal(replay.replayed, true); assert.equal(createCalls, 1); assert.equal(verifyCalls, 1); const newNonce = await enrollMoneroDepositAddress(allocator, rpc, context, signedRequest(randomUUID())); assert.equal(newNonce.allocationId, created.allocationId, 'one beneficiary keeps one address per wallet generation'); assert.equal(newNonce.replayed, true); assert.equal(createCalls, 1); await assert.rejects(enrollMoneroDepositAddress(allocator, rpc, context, { ...request, actor: other.actor }), /signature|beneficiary/); const wrongChain = { ...signedRequest(randomUUID()), chainId: 'c'.repeat(64) }; await assert.rejects(enrollMoneroDepositAddress(allocator, rpc, context, wrongChain), /does not match/); assert.equal(verifyCalls, 1, 'invalid signed terms must be rejected before Wallet RPC'); await assert.rejects(allocator.query('SELECT * FROM wk_custody.journals'), /permission denied/, 'the allocation role cannot read financial journals'); await assert.rejects(allocator.query('DELETE FROM wk_custody.monero_deposit_allocations WHERE allocation_id=$1', [created.allocationId]), /permission denied/); await assert.rejects(allocator.query(`INSERT INTO wk_custody.monero_deposit_allocations( allocation_id,request_id,custodian_id,market_network,market_chain_id,beneficiary,wallet_generation_id, wallet_fingerprint,monero_network,account_index,address_index,status,monero_address,request_digest,reserved_at,registered_at) VALUES($1,$2,$3,'wikideck-randomx-mainnet-1',$4,$5,$6,$7,'mainnet',0,100,'registered',$8,$9,$10,$10)` , [randomUUID(), randomUUID(), f.market.id, chainId, other.actor, walletGenerationId, walletFingerprint, await syntheticSubaddress(9), 'd'.repeat(64), now]), /inserted as address-free reservations/i, 'allocator credentials must not bypass the reserve-then-register state transition'); assert.equal((await f.admin.query('SELECT count(*)::int AS n FROM wk_custody.journals')).rows[0].n, 0, 'deposit address enrollment must not credit balances or post journals'); } finally { await allocator.end(); } }); test('Monero scan persistence is idempotent, reorg-visible and never posts customer credit', async t => { const f = await fixture(t); await f.admin.query("DO $$ BEGIN IF NOT EXISTS(SELECT 1 FROM pg_roles WHERE rolname='monero_observer') THEN CREATE ROLE monero_observer LOGIN; END IF; END $$"); await grantMoneroObserverRuntime(f.admin, 'monero_observer'); await assert.rejects(grantMoneroObserverRuntime(f.admin, 'custody_runtime'), /separate role/); const runtime = new Pool({ ...f.runtime, user: 'monero_observer' }); const custodyRuntime = new Pool(f.runtime), wallet = 'd'.repeat(64), scan1 = randomUUID(); const txid = 'e'.repeat(64), outputPublicKey = 'f'.repeat(64), observationId = `${txid}:${outputPublicKey}`; const observation = { observationId, transactionId: txid, amountAtomicXmr: '9007199254740993', height: 95, address: ADDITIONAL_MARKET_FEE.address, confirmations: 6, timestamp: 1_800_000_000, unlockTime: '0', accountIndex: 0, addressIndex: 4, outputPublicKey, globalOutputIndex: 123, locked: false, doubleSpendSeen: false }; const page = { network: 'mainnet' as const, walletHeight: 100, fromHeight: 0, throughHeight: 100, primaryAddress: ADDITIONAL_MARKET_FEE.address, incoming: [observation] }; try { await assert.rejects(custodyRuntime.query('SELECT * FROM wk_custody.monero_transfer_observations')); await assert.rejects(runtime.query('SELECT * FROM wk_custody.accounts')); const first = await persistMoneroObservationScan(runtime, { scanId: scan1, walletFingerprint: wallet, expectedScanHeight: 0, scannedAt: 1_800_000_100, page }); assert.equal(first.observed, 1); assert.equal(first.scanHeight, 100); assert.equal(first.replayed, false); const replay = await persistMoneroObservationScan(runtime, { scanId: scan1, walletFingerprint: wallet, expectedScanHeight: 0, scannedAt: 1_800_000_100, page }); assert.equal(replay.replayed, true); await assert.rejects(persistMoneroObservationScan(runtime, { scanId: scan1, walletFingerprint: wallet, expectedScanHeight: 0, scannedAt: 1_800_000_100, page: { ...page, incoming: [] } }), /reused/); const confirmationRefresh = await persistMoneroObservationScan(runtime, { scanId: randomUUID(), walletFingerprint: wallet, expectedScanHeight: 100, scannedAt: 1_800_000_150, page: { ...page, fromHeight: 90, walletHeight: 105, throughHeight: 105, incoming: [{ ...observation, confirmations: 11 }] } }); assert.equal(confirmationRefresh.changed, 0, 'normal confirmation growth is a projection update, not an immutable event per poll'); const missing = await persistMoneroObservationScan(runtime, { scanId: randomUUID(), walletFingerprint: wallet, expectedScanHeight: 105, scannedAt: 1_800_000_200, page: { ...page, fromHeight: 90, walletHeight: 110, throughHeight: 110, incoming: [] } }); assert.equal(missing.notSeen, 1); // Review signal only; never an automatic financial reversal. const reappeared = await persistMoneroObservationScan(runtime, { scanId: randomUUID(), walletFingerprint: wallet, expectedScanHeight: 110, scannedAt: 1_800_000_300, page: { ...page, fromHeight: 90, walletHeight: 120, throughHeight: 120, incoming: [{ ...observation, confirmations: 26 }] } }); assert.equal(reappeared.reappeared, 1); const observed = await runtime.query(`SELECT amount_atomic_xmr::text AS amount,monero_address,output_public_key,global_output_index,currently_present FROM wk_custody.monero_transfer_observations WHERE wallet_fingerprint=$1 AND observation_id=$2`, [wallet, observationId]); assert.equal(observed.rows[0].amount, '9007199254740993'); assert.equal(observed.rows[0].currently_present, true); assert.equal(observed.rows[0].monero_address, ADDITIONAL_MARKET_FEE.address); assert.equal(observed.rows[0].output_public_key, outputPublicKey); assert.equal(Number(observed.rows[0].global_output_index), 123); const reorgMovedOutput = await persistMoneroObservationScan(runtime, { scanId: randomUUID(), walletFingerprint: wallet, expectedScanHeight: 120, scannedAt: 1_800_000_325, page: { ...page, fromHeight: 90, walletHeight: 121, throughHeight: 121, incoming: [{ ...observation, height: 96, confirmations: 26 }] } }); assert.equal(reorgMovedOutput.changed, 1, 'same output re-included at a different height must be visible as changed evidence'); const updated = await runtime.query(`SELECT block_height,output_public_key FROM wk_custody.monero_transfer_observations WHERE wallet_fingerprint=$1 AND observation_id=$2`, [wallet, observationId]); assert.equal(Number(updated.rows[0].block_height), 96); assert.equal(updated.rows[0].output_public_key, outputPublicKey, 'output identity survives a reorg/re-inclusion'); await assert.rejects(persistMoneroObservationScan(runtime, { scanId: randomUUID(), walletFingerprint: wallet, expectedScanHeight: 121, scannedAt: 1_800_000_350, page: { ...page, fromHeight: 90, walletHeight: 121, throughHeight: 121, incoming: [{ ...observation, transactionId: 'a'.repeat(64), observationId: `${'a'.repeat(64)}:${outputPublicKey}`, height: 96, confirmations: 26 }] } }), /output key|unique/i, 'one wallet output key cannot be assigned to two transaction identities'); const eventTypes = await runtime.query('SELECT event_type FROM wk_custody.monero_observation_events ORDER BY sequence'); assert.deepEqual(eventTypes.rows.map(row => row.event_type), ['seen', 'not-seen', 'reappeared', 'changed']); await assert.rejects(runtime.query('UPDATE wk_custody.monero_observation_events SET event_type=\'seen\'')); await assert.rejects(runtime.query('DELETE FROM wk_custody.monero_observation_scans')); await assert.rejects(runtime.query('SELECT count(*) FROM wk_custody.journals'), /permission denied/); assert.equal((await f.admin.query('SELECT count(*)::int AS n FROM wk_custody.journals')).rows[0].n, 0, 'observation persistence must never create financial postings'); await assert.rejects(persistMoneroObservationScan(runtime, { scanId: randomUUID(), walletFingerprint: wallet, expectedScanHeight: 110, scannedAt: 1_800_000_400, page: { ...page, fromHeight: 90, walletHeight: 121, throughHeight: 121, incoming: [{ ...observation, confirmations: 27 }] } }), /cursor changed/); } finally { await custodyRuntime.end(); await runtime.end(); } }); test('Monero observer worker catches up through bounded durable windows, resumes, and admits one leader', async t => { const f = await fixture(t); await f.admin.query("DO $$ BEGIN IF NOT EXISTS(SELECT 1 FROM pg_roles WHERE rolname='monero_worker_test') THEN CREATE ROLE monero_worker_test LOGIN; END IF; END $$"); await grantMoneroObserverRuntime(f.admin, 'monero_worker_test'); const workerPool = new Pool({ ...config, user: 'monero_worker_test' }); const secondWorkerPool = new Pool({ ...config, user: 'monero_worker_test' }); const wallet = '7'.repeat(64), walletHeight = 250_005, now = Date.now(); const requested: number[] = []; const scanner = { async scanIncoming(fromHeight: number, maxBlocks = 100_000) { requested.push(fromHeight); const throughHeight = Math.min(walletHeight, fromHeight + maxBlocks - 1); return { network: 'mainnet' as const, walletHeight, fromHeight, throughHeight, primaryAddress: ADDITIONAL_MARKET_FEE.address, incoming: [] }; }, }; try { const failedScanner = { scanIncoming: async () => { throw new Error('fixture RPC failure'); } }; await assert.rejects(runMoneroObserverBatch(workerPool, wallet, failedScanner)); assert.equal((await f.admin.query('SELECT count(*)::int AS n FROM wk_custody.monero_observer_state WHERE wallet_fingerprint=$1', [wallet])).rows[0].n, 0, 'failed RPC must not create or advance durable cursor state'); const batch = await runMoneroObserverBatch(workerPool, wallet, scanner, { now: () => now }); assert.equal(batch.pages.length, 3, 'catch-up is split into a bounded number of pages'); assert.equal(batch.caughtUp, true); assert.equal(batch.scanHeight, walletHeight); assert.deepEqual(requested, [0, 98_999, 197_998]); const restarted = await runMoneroObserverBatch(secondWorkerPool, wallet, scanner, { now: () => now + 1, maxPages: 1 }); assert.equal(restarted.pages.length, 1); assert.equal(restarted.scanHeight, walletHeight); assert.equal(requested.at(-1), walletHeight - 1_000, 'restart resumes at a bounded reorg overlap'); const controller = new AbortController(); let signalReady!: () => void; const batchObserved = new Promise(resolve => { signalReady = resolve; }); const firstWorker = runMoneroObserverWorker(workerPool, wallet, scanner, { pollIntervalMs: 60_000, signal: controller.signal, onBatch: () => signalReady() }); await batchObserved; await assert.rejects(runMoneroObserverWorker(secondWorkerPool, wallet, scanner, { pollIntervalMs: 60_000 }), /already holds/); controller.abort(); await firstWorker; assert.equal((await f.admin.query('SELECT count(*)::int AS n FROM wk_custody.journals')).rows[0].n, 0, 'observer worker only persists chain observations and never posts money'); } finally { await secondWorkerPool.end(); await workerPool.end(); } }); test('reviewed Monero outputs post once to pending liability and reorgs create holds without reversal', async t => { const f = await fixture(t); await f.admin.query(`DO $$ BEGIN IF NOT EXISTS(SELECT 1 FROM pg_roles WHERE rolname='monero_observer') THEN CREATE ROLE monero_observer LOGIN; END IF; IF NOT EXISTS(SELECT 1 FROM pg_roles WHERE rolname='monero_deposit_credit') THEN CREATE ROLE monero_deposit_credit LOGIN; END IF; END $$`); await grantMoneroObserverRuntime(f.admin, 'monero_observer'); await grantMoneroDepositCreditRuntime(f.admin, 'monero_deposit_credit'); await assert.rejects(grantMoneroDepositCreditRuntime(f.admin, 'monero_deposit_allocator'), /separate role/); const creditPrivileges = await f.admin.query(`SELECT has_table_privilege('monero_deposit_credit', 'wk_custody.monero_deposit_credit_events','SELECT') AS can_read_credit, has_table_privilege('monero_deposit_credit','wk_custody.monero_observation_events','SELECT') AS can_read_events`); assert.equal(creditPrivileges.rows[0].can_read_credit, true); assert.equal(creditPrivileges.rows[0].can_read_events, true); const observer = new Pool({ ...config, user: 'monero_observer' }); const creditRuntime = new Pool({ ...config, user: 'monero_deposit_credit' }); const secondCreditRuntime = new Pool({ ...config, user: 'monero_deposit_credit' }); const custodyRuntime = new Pool(f.runtime); const wallet = '8'.repeat(64), txid = '9'.repeat(64), outputPublicKey = 'a'.repeat(64); const observationId = `${txid}:${outputPublicKey}`, now = Date.now(), scanId = randomUUID(); const generationId = randomUUID(), requestId = randomUUID(); const depositAddress = await syntheticSubaddress(10); const policy: DepositCreditPolicy = { approvedReviewerOwners: [host.actor], minimumConfirmations: 10, maximumObservationAgeMs: 120_000 }; try { const allocation = await reserveMoneroDepositAllocation(f.admin, { requestId, custodianId: f.market.id, marketNetwork: NETWORK, marketChainId: CHAIN_ID, beneficiary: buyer.actor, walletGenerationId: generationId, walletFingerprint: wallet, accountIndex: 0, reservedAt: now - 1 }); await registerMoneroDepositSubaddress(f.admin, { allocationId: allocation.allocation.allocationId, walletGenerationId: generationId, moneroAddress: depositAddress, registeredAt: now }); const allocationId = allocation.allocation.allocationId; const observation = { observationId, transactionId: txid, amountAtomicXmr: '9007199254740993', height: 90, address: depositAddress, confirmations: 11, timestamp: now, unlockTime: '0', accountIndex: 0, addressIndex: allocation.allocation.addressIndex, outputPublicKey, globalOutputIndex: 123, locked: false, doubleSpendSeen: false }; const page = { network: 'mainnet' as const, walletHeight: 100, fromHeight: 0, throughHeight: 100, primaryAddress: ADDITIONAL_MARKET_FEE.address, incoming: [observation] }; await persistMoneroObservationScan(observer, { scanId, walletFingerprint: wallet, expectedScanHeight: 0, scannedAt: now, page }); await assert.rejects(custodyRuntime.query('SELECT * FROM wk_custody.monero_transfer_observations')); await assert.rejects(observer.query('SELECT * FROM wk_custody.accounts')); await assert.rejects(creditRuntime.query('SELECT balance FROM wk_custody.accounts')); await assert.rejects(creditRuntime.query('UPDATE wk_custody.monero_transfer_observations SET currently_present=false')); await assert.rejects(creditRuntime.query('DELETE FROM wk_custody.journals')); await assert.rejects(creditRuntime.query('SELECT * FROM wk_custody.entries')); await assert.rejects(creditRuntime.query('UPDATE wk_custody.accounts SET balance=0')); await assert.rejects(prepareDepositCreditReview(creditRuntime, wallet, observationId, { ...policy, minimumConfirmations: 12 }, now), /not eligible/); const subject = await prepareDepositCreditReview(creditRuntime, wallet, observationId, policy, now); const nonce = randomUUID(), expiresAt = now + 60_000; const approval = { subject, reviewerOwner: host.actor, reviewerPublicKey: host.publicKey, nonce, expiresAt, signature: host.sign(depositCreditApprovalMessage(subject, host.actor, nonce, expiresAt)) }; const unapproved = { ...approval, reviewerOwner: buyer.actor, reviewerPublicKey: buyer.publicKey, signature: buyer.sign(depositCreditApprovalMessage(subject, buyer.actor, nonce, expiresAt)) }; await assert.rejects(postReviewedDepositPendingCredit(creditRuntime, unapproved, policy, now), /signature is invalid/); const expiredNonce = randomUUID(), expiredAt = now - 1; await assert.rejects(postReviewedDepositPendingCredit(creditRuntime, { ...approval, nonce: expiredNonce, expiresAt: expiredAt, signature: host.sign(depositCreditApprovalMessage(subject, host.actor, expiredNonce, expiredAt)) }, policy, now), /expired or outside/); const concurrent = await Promise.all([ postReviewedDepositPendingCredit(creditRuntime, approval, policy, now), postReviewedDepositPendingCredit(secondCreditRuntime, approval, policy, now), ]); assert.equal(concurrent.filter(result => !result.replayed).length, 1, 'parallel retries must commit one pending credit'); const first = concurrent.find(result => !result.replayed)!; assert.equal(first.amountAtomicXmr, observation.amountAtomicXmr); const replay = await postReviewedDepositPendingCredit(creditRuntime, approval, policy, now + 1); assert.equal(replay.replayed, true); assert.equal(replay.journalId, first.journalId); const lateReplay = await postReviewedDepositPendingCredit(creditRuntime, approval, policy, expiresAt + 1); assert.equal(lateReplay.replayed, true, 'a retry of an already committed review returns its receipt after expiry'); await assert.rejects(postReviewedDepositPendingCredit(creditRuntime, { ...approval, subject: { ...subject, amountAtomicXmr: '1' } }, policy, now + 2), /signature/); const pendingId = `customer:${buyer.actor}:deposit-pending:${first.creditId}`; const posted = await f.admin.query('SELECT id,normal,balance::text AS balance FROM wk_custody.accounts WHERE id=ANY($1::text[])', [[pendingId, 'treasury:monero:pending', `customer:${buyer.actor}:available`]]); assert.deepEqual(posted.rows.map(row => [row.id, row.normal, row.balance]).sort((a,b) => String(a[0]).localeCompare(String(b[0]))), [ [pendingId, 'credit', observation.amountAtomicXmr], ['treasury:monero:pending', 'debit', observation.amountAtomicXmr], ].sort((a,b) => String(a[0]).localeCompare(String(b[0]))), 'pending credit must never enter spendable available balance'); assert.equal((await f.admin.query("SELECT count(*)::int AS n FROM wk_custody.journals WHERE kind='monero-deposit-pending-credit'")).rows[0].n, 1); assert.equal((await f.market.audit()).assetsAtomicXmr, observation.amountAtomicXmr); const missing = await persistMoneroObservationScan(observer, { scanId: randomUUID(), walletFingerprint: wallet, expectedScanHeight: 100, scannedAt: now + 100, page: { ...page, fromHeight: 90, walletHeight: 101, throughHeight: 101, incoming: [] } }); assert.equal(missing.notSeen, 1); assert.equal(await reconcileDepositCreditReorgHolds(creditRuntime, wallet, now + 110), 1); assert.equal(await reconcileDepositCreditReorgHolds(creditRuntime, wallet, now + 111), 0, 'hold replay is idempotent'); const reappeared = await persistMoneroObservationScan(observer, { scanId: randomUUID(), walletFingerprint: wallet, expectedScanHeight: 101, scannedAt: now + 200, page: { ...page, fromHeight: 90, walletHeight: 102, throughHeight: 102, incoming: [{ ...observation, confirmations: 13 }] } }); assert.equal(reappeared.reappeared, 1); assert.equal(await reconcileDepositCreditReorgHolds(creditRuntime, wallet, now + 210), 1); assert.equal((await f.admin.query('SELECT count(*)::int AS n FROM wk_custody.monero_deposit_reorg_holds')).rows[0].n, 2); assert.equal((await f.admin.query('SELECT count(*)::int AS n FROM wk_custody.journals')).rows[0].n, 1, 'reorg/reappearance must not reverse or duplicate the pending credit journal'); assert.equal((await f.admin.query('SELECT balance::text FROM wk_custody.accounts WHERE id=$1', [pendingId])).rows[0].balance, observation.amountAtomicXmr); await assert.rejects(creditRuntime.query('UPDATE wk_custody.monero_deposit_credit_events SET amount_atomic_xmr=1')); await assert.rejects(creditRuntime.query('DELETE FROM wk_custody.monero_deposit_reorg_holds')); const releaseSubject = await prepareDepositCreditReleaseReview(creditRuntime, first.creditId, policy, now + 220); assert.equal(releaseSubject.unresolvedHolds.length, 2, 'release review must include prior disappearance and reappearance evidence'); const releaseNonce = randomUUID(), releaseExpiresAt = now + 280; const releaseApproval = { subject: releaseSubject, reviewerOwner: host.actor, reviewerPublicKey: host.publicKey, nonce: releaseNonce, expiresAt: releaseExpiresAt, signature: host.sign(depositCreditReleaseApprovalMessage(releaseSubject, host.actor, releaseNonce, releaseExpiresAt)) }; const releases = await Promise.all([ releaseReviewedDepositCredit(creditRuntime, releaseApproval, policy, now + 221), releaseReviewedDepositCredit(secondCreditRuntime, releaseApproval, policy, now + 221), ]); assert.equal(releases.filter(item => !item.replayed).length, 1); const released = releases.find(item => !item.replayed)!; assert.equal(released.amountAtomicXmr, observation.amountAtomicXmr); const releaseReplay = await releaseReviewedDepositCredit(creditRuntime, releaseApproval, policy, releaseExpiresAt + 1); assert.equal(releaseReplay.replayed, true, 'exact release retry returns the committed result after signature expiry'); assert.equal((await f.admin.query('SELECT balance::text FROM wk_custody.accounts WHERE id=$1', [pendingId])).rows[0].balance, '0'); assert.equal((await f.admin.query('SELECT balance::text FROM wk_custody.accounts WHERE id=$1', [`customer:${buyer.actor}:available`])).rows[0].balance, observation.amountAtomicXmr); assert.equal((await f.admin.query('SELECT count(*)::int AS n FROM wk_custody.monero_deposit_reorg_resolutions WHERE release_id=$1', [released.releaseId])).rows[0].n, 2); assert.equal((await f.market.audit()).assetsAtomicXmr, observation.amountAtomicXmr); const laterMissing = await persistMoneroObservationScan(observer, { scanId: randomUUID(), walletFingerprint: wallet, expectedScanHeight: 102, scannedAt: now + 300, page: { ...page, fromHeight: 90, walletHeight: 103, throughHeight: 103, incoming: [] } }); assert.equal(laterMissing.notSeen, 1); assert.equal(await reconcileDepositCreditReorgHolds(creditRuntime, wallet, now + 310), 1); await assert.rejects(f.execute(buyer, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '1', maxFeeAtomicXmr: '0' }), /reorg review/); const laterReappeared = await persistMoneroObservationScan(observer, { scanId: randomUUID(), walletFingerprint: wallet, expectedScanHeight: 103, scannedAt: now + 400, page: { ...page, fromHeight: 90, walletHeight: 104, throughHeight: 104, incoming: [{ ...observation, confirmations: 15 }] } }); assert.equal(laterReappeared.reappeared, 1); assert.equal(await reconcileDepositCreditReorgHolds(creditRuntime, wallet, now + 410), 1); const unresolved = await f.admin.query(`SELECT hold_id::text FROM wk_custody.monero_deposit_reorg_holds h WHERE h.credit_id=$1 AND NOT EXISTS(SELECT 1 FROM wk_custody.monero_deposit_reorg_resolutions r WHERE r.hold_id=h.hold_id) ORDER BY h.observation_event_sequence`, [first.creditId]); assert.equal(unresolved.rowCount, 2, 'both the later disappearance and reappearance events require explicit resolution'); let resolutionClock = now + 420; for (const hold of unresolved.rows) { const resolutionSubject = await prepareDepositReorgResolution(creditRuntime, first.creditId, hold.hold_id, policy, resolutionClock); const resolutionNonce = randomUUID(), resolutionExpiresAt = resolutionClock + 60; const resolutionApproval = { subject: resolutionSubject, reviewerOwner: host.actor, reviewerPublicKey: host.publicKey, nonce: resolutionNonce, expiresAt: resolutionExpiresAt, signature: host.sign(depositReorgResolutionMessage(resolutionSubject, host.actor, resolutionNonce, resolutionExpiresAt)) }; const resolved = await resolveReleasedDepositReorgHold(creditRuntime, resolutionApproval, policy, resolutionClock + 1); assert.equal(resolved.replayed, false); assert.equal((await resolveReleasedDepositReorgHold(creditRuntime, resolutionApproval, policy, resolutionClock + 2)).replayed, true); resolutionClock += 10; } assert.equal((await f.admin.query(`SELECT count(*)::int AS n FROM wk_custody.monero_deposit_reorg_holds h WHERE h.credit_id=$1 AND NOT EXISTS(SELECT 1 FROM wk_custody.monero_deposit_reorg_resolutions r WHERE r.hold_id=h.hold_id)`, [first.creditId])).rows[0].n, 0); await f.execute(buyer, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '1', maxFeeAtomicXmr: '0' }); assert.equal((await f.admin.query('SELECT count(*)::int AS n FROM wk_custody.journals')).rows[0].n, 3, 'one pending credit, one release, and one authorized sandbox withdrawal are journaled'); assert.equal((await f.market.audit()).assetsAtomicXmr, observation.amountAtomicXmr); } finally { await custodyRuntime.end(); await secondCreditRuntime.end(); await creditRuntime.end(); await observer.end(); } }); test('database rejects unbalanced, fractional, negative, historical append and oversized postings at commit', async t => { const f = await fixture(t); await f.execute(buyer, { type: 'mock-credit' }); const c = await f.admin.connect(); try { await c.query('BEGIN'); await c.query("INSERT INTO wk_custody.journals(id,kind,created) VALUES('bad','test',0)"); await c.query("INSERT INTO wk_custody.entries VALUES('bad',0,'treasury:mock',1,0)"); await assert.rejects(c.query('COMMIT'), /Unbalanced/); await c.query('ROLLBACK'); await assert.rejects(c.query("INSERT INTO wk_custody.entries VALUES($1,9,'treasury:mock',1,0)", [`credit:${buyer.actor}`]), /committed/); for (const amount of ['-1', '1.5', '1000000000000000000000000000000']) await assert.rejects(c.query('INSERT INTO wk_custody.accounts VALUES($1,$2,$3)', [randomUUID(), 'debit', amount])); } finally { c.release(); } assert.equal((await f.market.audit()).journals, 1); }); test('two independent pools serialize duplicate receipts, competing reservations and withdrawals without overspending', async t => { const f = await fixture(t), second = await PostgresCustody.open(f.runtime, options); try { const credit = envelope(f.market.id, buyer, { type: 'mock-credit' }); const credits = await Promise.all([f.market.execute(credit), second.execute(credit)]); assert.deepEqual(credits[0], credits[1]); const listing = await f.execute(seller, { type: 'list', cardHash: hash, sequence: 0, priceAtomicXmr: '800000000000', feeBps: 250 }) as CustodyListing; await f.execute(other, { type: 'mock-credit' }); const races = await Promise.allSettled([f.market.execute(envelope(f.market.id, buyer, { type: 'reserve', listingId: listing.id, priceAtomicXmr: listing.priceAtomicXmr, feeBps: 250 })), second.execute(envelope(f.market.id, other, { type: 'reserve', listingId: listing.id, priceAtomicXmr: listing.priceAtomicXmr, feeBps: 250 }))]); assert.equal(races.filter(r => r.status === 'fulfilled').length, 1); const winner = races.find(r => r.status === 'fulfilled') as PromiseFulfilledResult; assert.equal(winner.value.sellerAtomicXmr, '772000000000'); assert.equal((await f.activity(seller)).availableAtomicXmr, '0'); // No unimplemented NFT completion credit. const loser = winner.value.buyer === buyer.actor ? other : buyer; const withdrawals = await Promise.allSettled([f.market.execute(envelope(f.market.id, loser, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '900000000000', maxFeeAtomicXmr: '1000' })), second.execute(envelope(f.market.id, loser, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '900000000000', maxFeeAtomicXmr: '1000' }))]); assert.equal(withdrawals.filter(r => r.status === 'fulfilled').length, 1); assert.equal((await f.activity(loser)).withdrawalReservedAtomicXmr, '900000001000'); assert.equal((await f.market.audit()).assetsAtomicXmr, '2000000000000'); assert.equal((await f.activity(identity())).orders.length, 0); } finally { await second.close(); } }); test('cancel/replay/reconnect preserves exact money, and receipt-insert failure rolls back balance and outbox', async t => { const f = await fixture(t); await f.execute(buyer, { type: 'mock-credit' }); const w = await f.execute(buyer, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '123', maxFeeAtomicXmr: '5' }) as CustodyWithdrawal; const cancel = envelope(f.market.id, buyer, { type: 'cancel-withdrawal', withdrawalId: w.id }); await f.market.execute(cancel); const reopened = await PostgresCustody.open(f.runtime, options); try { assert.equal(reopened.id, f.market.id); assert.deepEqual(await reopened.execute(cancel), await f.market.execute(cancel)); await assert.rejects(reopened.execute(envelope(f.market.id, buyer, { type: 'mock-credit' }, cancel.nonce)), /Nonce/); await f.admin.query("CREATE FUNCTION wk_custody.inject_failure() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN RAISE EXCEPTION 'fixture failure'; END $$; CREATE TRIGGER fail_receipt BEFORE INSERT ON wk_custody.receipts FOR EACH ROW EXECUTE FUNCTION wk_custody.inject_failure()"); await assert.rejects(f.execute(buyer, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '200', maxFeeAtomicXmr: '0' })); assert.equal((await f.activity()).availableAtomicXmr, '1000000000000'); assert.equal((await f.activity()).withdrawals.length, 1); await f.admin.query('DROP TRIGGER fail_receipt ON wk_custody.receipts'); assert.equal((await f.market.audit()).journals, 3); } finally { await reopened.close(); } }); test('exact amounts above JS safe integer survive NUMERIC, JSON signatures and audit', async t => { const f = await fixture(t); // Seed a balanced synthetic fixture through SQL, not an HTTP mint/admin endpoint. const c = await f.admin.connect(), large = '9007199254740993'; try { await c.query('BEGIN'); await c.query("INSERT INTO wk_custody.accounts(id,normal) VALUES('treasury:mock','debit'),($1,'credit')", [`customer:${buyer.actor}:available`]); await c.query("INSERT INTO wk_custody.journals(id,kind,created) VALUES('large','fixture',0)"); await c.query("INSERT INTO wk_custody.entries VALUES('large',0,'treasury:mock',$1,0),('large',1,$2,0,$1)", [large, `customer:${buyer.actor}:available`]); await c.query('COMMIT'); } finally { c.release(); } assert.equal((await f.activity()).availableAtomicXmr, large); await f.execute(buyer, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: large, maxFeeAtomicXmr: '0' }); assert.equal((await f.activity()).withdrawalReservedAtomicXmr, large); assert.equal((await f.market.audit()).assetsAtomicXmr, large); }); test('SQLite cutover preserves identity, history, prepared attempts, ordering and replay; source cannot restart', async t => { const f = await fixture(t); // Import requires truly empty target, even a previously initialized empty custodian is rejected. await f.admin.query('DELETE FROM wk_custody.custody_meta'); const dir = await mkdtemp(join(tmpdir(), 'custody-pg-import-')); t.after(() => rm(dir, { recursive: true, force: true })); const source = await CustodyMarket.open(dir, options); const originalId = source.id, credit = envelope(originalId, buyer, { type: 'mock-credit' }); source.execute(credit); const w = source.execute(envelope(originalId, buyer, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '12345', maxFeeAtomicXmr: '100' })) as CustodyWithdrawal; const attempt = randomUUID(), tx = `fixture:${'b'.repeat(64)}`; source.prepareWithdrawalFixture(w.id, attempt, tx, '50'); const listing = source.execute(envelope(originalId, seller, { type: 'list', cardHash: hash, sequence: 0, priceAtomicXmr: '10000', feeBps: 250 })) as CustodyListing; const order = source.execute(envelope(originalId, buyer, { type: 'reserve', listingId: listing.id, priceAtomicXmr: listing.priceAtomicXmr, feeBps: 250 })) as CustodyOrder; const oldAudit = source.audit(); source.close(); const file = join(dir, 'testnet', 'custody-sandbox.sqlite'); const imported = await importSqliteCustody(file, f.admin); assert.equal(imported.custodianId, originalId); assert.deepEqual(await importSqliteCustody(file, f.admin), imported); await assert.rejects(CustodyMarket.open(dir, options), /frozen/); const pg = await PostgresCustody.open(f.runtime, options); try { assert.deepEqual(await pg.audit(), oldAudit); assert.equal((await pg.execute(credit) as CustodyActivity).availableAtomicXmr, '1000000000000'); // Original receipt, not current balance. const activity = await pg.execute(envelope(originalId, buyer, { type: 'activity', offset: 0 })) as CustodyActivity; assert.equal(activity.withdrawals[0].attemptId, attempt); assert.equal(activity.withdrawals[0].transactionId, tx); assert.equal(activity.orders[0].id, order.id); await pg.execute(envelope(originalId, buyer, { type: 'cancel-reservation', orderId: order.id })); assert.equal((await pg.execute(envelope(originalId, buyer, { type: 'activity', offset: 0 })) as CustodyActivity).purchaseReservedAtomicXmr, '0'); await assert.rejects(pg.execute(envelope(originalId, buyer, { type: 'cancel-withdrawal', withdrawalId: w.id })), /unprepared/); } finally { await pg.close(); } const sqlite = new DatabaseSync(file); try { sqlite.prepare("UPDATE accounts SET balance='1' WHERE id=?").run(`customer:${buyer.actor}:available`); } finally { sqlite.close(); } await assert.rejects(importSqliteCustody(file, f.admin), /source changed/); }); test('PostgreSQL HTTP custody integration, safe storage binding and backup refusal use the real runtime role', { timeout: 45000 }, async t => { const f = await fixture(t), dir = await mkdtemp(join(tmpdir(), 'custody-pg-http-')); const urlFile = join(dir, 'pg-url'); await writeFile(urlFile, 'postgresql://custody_runtime@127.0.0.1/custody_test', { mode: 0o600 }); const env = { ...process.env, HOST: '127.0.0.1', PORT: '0', NODE_ENV: 'test', WIKIDECK_DATA_DIR: dir, WIKIDECK_CUSTODY_MODE: 'sandbox', WIKIDECK_CUSTODY_STORAGE: 'postgres', WIKIDECK_CUSTODY_PG_URL_FILE: urlFile, WIKIDECK_CUSTODY_PG_LOCAL_TEST: '1', WIKIDECK_PG_DISPOSABLE_TEST: '1', WIKIDECK_CUSTODY_CANONICAL_REHEARSAL: '1', WIKIDECK_PAYMENT_MODE: 'mock', WIKIDECK_PEERS: '' }; const child = spawn(process.execPath, ['--experimental-strip-types', 'server/index.ts'], { env, stdio: ['ignore', 'pipe', 'pipe'] }); const closed = new Promise(resolve => child.once('close', () => resolve())); t.after(async () => { if (child.exitCode === null) child.kill('SIGTERM'); await closed; await rm(dir, { recursive: true, force: true }); }); const base = await new Promise((resolve, reject) => { let log = ''; const timer = setTimeout(() => reject(new Error('HTTP startup timeout')), 25000); const listener = (chunk: Buffer) => { log += chunk.toString(); const m = /Wikideck RandomX testnet: (http:\/\/127\.0\.0\.1:\d+)/.exec(log); if (m) { clearTimeout(timer); resolve(m[1]); } }; child.stdout.on('data', listener); child.stderr.on('data', listener); child.once('exit', () => { clearTimeout(timer); reject(new Error('HTTP startup failed')); }); }); const page = await (await fetch(`${base}/api/custody`)).json(); assert.equal(page.info.custodianId, f.market.id); const observerStream = await f.admin.query("SELECT value FROM wk_custody.custody_meta WHERE key='canonical-inbox:v1:stream'"); assert.match(observerStream.rows[0].value, /^[a-f0-9-]{36}$/); const command = envelope(f.market.id, buyer, { type: 'mock-credit' }); const response = await fetch(`${base}/api/custody/command`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(command) }); assert.equal(response.status, 200); assert.equal((await response.json()).availableAtomicXmr, '1000000000000'); assert.match(response.headers.get('cache-control') ?? '', /no-store/); assert.equal(JSON.parse(await readFile(join(dir, 'testnet', 'custody-postgres.json'), 'utf8')).custodianId, f.market.id); child.kill('SIGTERM'); await closed; assert.equal(child.exitCode, 0, 'opt-in observer should drain before custody shutdown'); await assert.rejects(createBackup(dir, join(dir, '..', `refused-${randomUUID()}`)), /coordinated/); await assert.rejects(openCustodyStorage(dir, options, { WIKIDECK_CUSTODY_STORAGE: 'sqlite' }), /fallback prohibited/); }); test('configuration requires secret files, verified TLS, literal backend, and loopback-only plaintext test transport', async t => { const dir = await mkdtemp(join(tmpdir(), 'custody-pg-config-')); t.after(() => rm(dir, { recursive: true, force: true })); const file = join(dir, 'connection'); assert.equal(custodyStorage({}), 'sqlite'); assert.throws(() => custodyStorage({ WIKIDECK_CUSTODY_STORAGE: 'other' })); await assert.rejects(openCustodyStorage(dir, options, { WIKIDECK_CUSTODY_STORAGE: 'sqlite', WIKIDECK_CUSTODY_CANONICAL_REHEARSAL: '1', WIKIDECK_PG_DISPOSABLE_TEST: '1' }), /requires PostgreSQL/); await assert.rejects(openCustodyStorage(dir, options, { WIKIDECK_CUSTODY_STORAGE: 'postgres', WIKIDECK_CUSTODY_CANONICAL_REHEARSAL: '1' }), /disposable database test guard/); await assert.rejects(openCustodyStorage(dir, options, { NODE_ENV: 'production', WIKIDECK_CUSTODY_STORAGE: 'postgres', WIKIDECK_CUSTODY_CANONICAL_REHEARSAL: '1', WIKIDECK_PG_DISPOSABLE_TEST: '1' }), /requires PostgreSQL/); await assert.rejects(openCustodyStorage(dir, options, { WIKIDECK_CUSTODY_STORAGE: 'postgres', WIKIDECK_CUSTODY_CANONICAL_REHEARSAL: 'yes', WIKIDECK_PG_DISPOSABLE_TEST: '1' }), /exactly 0 or 1/); await assert.rejects(postgresConfig({}), /configuration/); await writeFile(file, 'postgresql://user:secret@database.example/custody', { mode: 0o600 }); const secure = await postgresConfig({ WIKIDECK_CUSTODY_PG_URL_FILE: file }); assert.equal((secure.ssl as { rejectUnauthorized: boolean }).rejectUnauthorized, true); await assert.rejects(postgresConfig({ WIKIDECK_CUSTODY_PG_URL_FILE: file, WIKIDECK_CUSTODY_PG_LOCAL_TEST: '1' }), /configuration/); await writeFile(file, 'postgresql://user:secret@127.0.0.1/custody?sslmode=disable'); await assert.rejects(postgresConfig({ WIKIDECK_CUSTODY_PG_URL_FILE: file }), /configuration/); }); test('PostgreSQL market disable preserves existing claims and signed withdrawal cancellation', async t => { const f = await fixture(t); await f.execute(buyer, { type: 'mock-credit' }); const listing = await f.execute(seller, { type: 'list', cardHash: hash, sequence: 0, priceAtomicXmr: '100', feeBps: 250 }) as CustodyListing; const order = await f.execute(buyer, { type: 'reserve', listingId: listing.id, priceAtomicXmr: '100', feeBps: 250 }) as CustodyOrder; const disabled = await PostgresCustody.open(f.runtime, { ...options, marketEnabled: false }); try { assert.equal(disabled.info().marketEnabled, false); assert.deepEqual((await disabled.browse()).listings, []); await assert.rejects(disabled.execute(envelope(disabled.id, other, { type: 'mock-credit' })), /disabled/); await disabled.execute(envelope(disabled.id, buyer, { type: 'cancel-reservation', orderId: order.id })); const w = await disabled.execute(envelope(disabled.id, buyer, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '1', maxFeeAtomicXmr: '0' })) as CustodyWithdrawal; await disabled.execute(envelope(disabled.id, buyer, { type: 'cancel-withdrawal', withdrawalId: w.id })); assert.equal((await disabled.audit()).assetsAtomicXmr, '1000000000000'); } finally { await disabled.close(); } }); test('full PostgreSQL dump/isolated restore preserves custody identity, receipts and journal backing', async t => { const f = await fixture(t); await f.execute(buyer, { type: 'mock-credit' }); const cmd = envelope(f.market.id, buyer, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '500', maxFeeAtomicXmr: '20' }); const original = await f.market.execute(cmd), audit = await f.market.audit(); const dir = await mkdtemp(join(tmpdir(), 'custody-pg-restore-')), archive = join(dir, 'custody.dump'); const run = promisify(execFile), database = `restore_${randomUUID().replaceAll('-', '')}`; try { // Fixture role and TCP transport are local/trust-only on the isolated tmpfs network. No production credentials. await run('pg_dump', ['-h', '127.0.0.1', '-U', 'postgres', '-d', 'custody_test', '-Fc', '-f', archive]); await f.admin.query(`CREATE DATABASE "${database}"`); await run('pg_restore', ['-h', '127.0.0.1', '-U', 'postgres', '-d', database, '--exit-on-error', archive]); const restored = await PostgresCustody.open({ ...f.runtime, database }, options); try { assert.equal(restored.id, f.market.id); assert.deepEqual(await restored.audit(), audit); assert.deepEqual(await restored.execute(cmd), original); const balance = await restored.execute(envelope(restored.id, buyer, { type: 'activity', offset: 0 })) as CustodyActivity; assert.equal(balance.availableAtomicXmr, '999999999480'); assert.equal(balance.withdrawalReservedAtomicXmr, '520'); } finally { await restored.close(); } } finally { await f.admin.query(`DROP DATABASE IF EXISTS "${database}" WITH (FORCE)`); await rm(dir, { recursive: true, force: true }); } }); test('coordinated node/PostgreSQL backup holds both writer fences and verifies matching custodian identity', async t => { const f = await fixture(t); await f.execute(buyer, { type: 'mock-credit' }); const dir = await mkdtemp(join(tmpdir(), 'wikideck-coordinated-backup-')); t.after(() => rm(dir, { recursive: true, force: true })); const data = join(dir, 'node-data'), networkDir = join(data, 'testnet'); await mkdir(networkDir, { recursive: true }); 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> = 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)'); marketDb.prepare('INSERT INTO market_meta VALUES(?,?)').run('identity', JSON.stringify({ version: 1, network: NETWORK, chainId: CHAIN_ID, id: randomUUID() })); } finally { marketDb.close(); } await writeFile(join(networkDir, 'custody-postgres.json'), JSON.stringify({ version: 1, custodianId: f.market.id }), { mode: 0o600 }); // Model a legitimate post-cutover node: its frozen SQLite source is historical and PostgreSQL is authoritative. const cutoverDigest = 'a'.repeat(64), legacyDb = new DatabaseSync(join(networkDir, 'custody-sandbox.sqlite')); try { legacyDb.exec('CREATE TABLE custody_meta(key TEXT PRIMARY KEY,value TEXT NOT NULL)'); legacyDb.prepare('INSERT INTO custody_meta VALUES(?,?)').run('postgres_cutover', cutoverDigest); } finally { legacyDb.close(); } await f.admin.query('INSERT INTO wk_custody.custody_meta(key,value) VALUES($1,$2)', ['import_source', cutoverDigest]); const runtimeUrl = join(dir, 'runtime-url'), backupUrl = join(dir, 'backup-url'); await writeFile(runtimeUrl, 'postgresql://custody_runtime@127.0.0.1/custody_test', { mode: 0o600 }); const backupRole = `custody_backup_${randomUUID().replaceAll('-', '')}`; await f.admin.query(`CREATE ROLE ${backupRole} LOGIN; GRANT CONNECT ON DATABASE custody_test TO ${backupRole}; GRANT USAGE ON SCHEMA wk_custody TO ${backupRole}; GRANT SELECT ON ALL TABLES IN SCHEMA wk_custody TO ${backupRole}; GRANT SELECT ON ALL SEQUENCES IN SCHEMA wk_custody TO ${backupRole}`); await writeFile(backupUrl, `postgresql://${backupRole}@127.0.0.1/custody_test`, { mode: 0o600 }); const env = { ...process.env, WIKIDECK_CUSTODY_PG_URL_FILE: runtimeUrl, WIKIDECK_CUSTODY_PG_BACKUP_URL_FILE: backupUrl, WIKIDECK_CUSTODY_PG_LOCAL_TEST: '1' }; const destination = join(dir, 'paired-backup'); const inheritedLibpq = { hostAddr: process.env.PGHOSTADDR, sslMode: process.env.PGSSLMODE, sslRoot: process.env.PGSSLROOTCERT }; let manifest: Awaited>; try { process.env.PGHOSTADDR = '192.0.2.123'; process.env.PGSSLMODE = 'disable'; process.env.PGSSLROOTCERT = 'invalid-inherited-root.pem'; manifest = await createCoordinatedCustodyBackup(data, destination, env); } finally { if (inheritedLibpq.hostAddr === undefined) delete process.env.PGHOSTADDR; else process.env.PGHOSTADDR = inheritedLibpq.hostAddr; if (inheritedLibpq.sslMode === undefined) delete process.env.PGSSLMODE; else process.env.PGSSLMODE = inheritedLibpq.sslMode; if (inheritedLibpq.sslRoot === undefined) delete process.env.PGSSLROOTCERT; else process.env.PGSSLROOTCERT = inheritedLibpq.sslRoot; } assert.equal(manifest.custodianId, f.market.id); assert.equal(manifest.database, 'custody_test'); assert.match(manifest.postgresServerVersion, /^\d+\.\d+/); assert.match(manifest.postgresServerVersionNum, /^\d{5,6}$/); assert.match(manifest.pgDumpVersion, /^pg_dump \(PostgreSQL\) /); assert.match(manifest.pgRestoreVersion, /^pg_restore \(PostgreSQL\) /); assert.equal(manifest.sqliteCutoverSourceDigest, cutoverDigest); assert.equal((await verifyCoordinatedCustodyBackup(destination)).nodeManifestSha256, manifest.nodeManifestSha256); if (process.platform !== 'win32') { const oldRestore = join(dir, 'old-pg-restore'); await writeFile(oldRestore, '#!/bin/sh\nprintf "pg_restore (PostgreSQL) 9.6\\n"\n', { mode: 0o700 }); await chmod(oldRestore, 0o700); await assert.rejects(verifyCoordinatedCustodyBackup(destination, oldRestore), /older than archive pg_dump/); } const extraNodeFile = join(destination, 'node', 'unmanifested.txt'); await writeFile(extraNodeFile, 'not part of backup manifest'); await assert.rejects(verifyCoordinatedCustodyBackup(destination), /Unexpected coordinated node backup contents/); await rm(extraNodeFile); 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 { await run('pg_restore', ['-h', '127.0.0.1', '-U', 'postgres', '-d', restoreDatabase, '--exit-on-error', join(destination, 'custody.dump')]); const restored = await PostgresCustody.open({ ...f.runtime, database: restoreDatabase }, options); try { assert.equal(restored.id, f.market.id); assert.deepEqual(await restored.audit(), await f.market.audit()); assert.equal(await restored.importSource(), cutoverDigest); const activity = await restored.execute(envelope(restored.id, buyer, { type: 'activity', offset: 0 })) as CustodyActivity; assert.equal(activity.availableAtomicXmr, '1000000000000'); } finally { await restored.close(); } } finally { await f.admin.query(`DROP DATABASE IF EXISTS "${restoreDatabase}" WITH (FORCE)`); } const backupRolePool = new Pool({ ...config, user: backupRole }); try { await assert.rejects(backupRolePool.query("UPDATE wk_custody.accounts SET balance=0")); await assert.rejects(backupRolePool.query("DELETE FROM wk_custody.entries")); await assert.rejects(backupRolePool.query("SELECT nextval('wk_custody.journals_sequence_seq')")); } finally { await backupRolePool.end(); } assert.equal((await f.market.audit()).assetsAtomicXmr, '1000000000000'); await assert.rejects(createCoordinatedCustodyBackup(data, destination, env), /already exists/); const staleLock = join(data, 'testnet-node.lock'), lockedDestination = join(dir, 'locked-backup'); await writeFile(staleLock, 'operator-lock'); await assert.rejects(createCoordinatedCustodyBackup(data, lockedDestination, env), /Node directory is locked/); await assert.rejects(lstat(lockedDestination)); await rm(staleLock); const failedDump = join(dir, 'failed-dump'), brokenEnv = { ...env, WIKIDECK_PG_DUMP_BINARY: join(dir, 'missing-pg-dump') }; await assert.rejects(createCoordinatedCustodyBackup(data, failedDump, brokenEnv), /client utility failed to start/); await assert.rejects(verifyCoordinatedCustodyBackup(failedDump), /ENOENT/); // A failed external dump releases PostgreSQL's shared-writer fence; normal sandbox commands still work. const recoveredWrite = await f.execute(other, { type: 'mock-credit' }) as CustodyActivity; assert.equal(recoveredWrite.availableAtomicXmr, '1000000000000'); const mismatchedBinding = join(dir, 'mismatch-binding'), bindingPath = join(networkDir, 'custody-postgres.json'); const originalBinding = await readFile(bindingPath, 'utf8'); await writeFile(bindingPath, JSON.stringify({ version: 1, custodianId: randomUUID() }), { mode: 0o600 }); await assert.rejects(createCoordinatedCustodyBackup(data, mismatchedBinding, env), /identities differ/); await writeFile(bindingPath, originalBinding, { mode: 0o600 }); await assert.rejects(verifyCoordinatedCustodyBackup(mismatchedBinding), /ENOENT/); const corruptDump = join(destination, 'custody.dump'); await writeFile(corruptDump, 'corrupt'); await assert.rejects(verifyCoordinatedCustodyBackup(destination), /hash\/size mismatch/); // Even if an operator or attacker rewrites the unsigned manifest hashes, pg_restore parsing rejects junk bytes. const manifestPath = join(destination, 'manifest.json'), altered = JSON.parse(await readFile(manifestPath, 'utf8')); altered.postgresDump.bytes = Buffer.byteLength('corrupt'); altered.postgresDump.sha256 = createHash('sha256').update('corrupt').digest('hex'); await writeFile(manifestPath, JSON.stringify(altered)); await assert.rejects(verifyCoordinatedCustodyBackup(destination), /dump archive is invalid/); const unrelatedDatabase = `empty_${randomUUID().replaceAll('-', '')}`, unrelatedDump = join(dir, 'unrelated.dump'); await f.admin.query(`CREATE DATABASE "${unrelatedDatabase}"`); try { await run('pg_dump', ['-h', '127.0.0.1', '-U', 'postgres', '-d', unrelatedDatabase, '-Fc', '-f', unrelatedDump]); await writeFile(corruptDump, await readFile(unrelatedDump)); const validButWrong = JSON.parse(await readFile(manifestPath, 'utf8')); const unrelated = await readFile(unrelatedDump); validButWrong.postgresDump.bytes = unrelated.length; validButWrong.postgresDump.sha256 = createHash('sha256').update(unrelated).digest('hex'); await writeFile(manifestPath, JSON.stringify(validButWrong)); await assert.rejects(verifyCoordinatedCustodyBackup(destination), /missing required custody accounting tables/); } finally { await f.admin.query(`DROP DATABASE IF EXISTS "${unrelatedDatabase}" WITH (FORCE)`); } }); test('failed cutover freezes the source, rolls back the entire target, and resumes without duplicate balances', async t => { const f = await fixture(t); await f.admin.query('DELETE FROM wk_custody.custody_meta'); const dir = await mkdtemp(join(tmpdir(), 'custody-pg-failed-import-')); t.after(() => rm(dir, { recursive: true, force: true })); const source = await CustodyMarket.open(dir, options); source.execute(envelope(source.id, buyer, { type: 'mock-credit' })); source.close(); const file = join(dir, 'testnet', 'custody-sandbox.sqlite'); await f.admin.query("CREATE FUNCTION wk_custody.import_failure() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN RAISE EXCEPTION 'injected import failure'; END $$; CREATE TRIGGER fail_import BEFORE INSERT ON wk_custody.receipts FOR EACH ROW EXECUTE FUNCTION wk_custody.import_failure()"); await assert.rejects(importSqliteCustody(file, f.admin), /injected import/); assert.equal((await f.admin.query('SELECT count(*)::integer AS n FROM wk_custody.accounts')).rows[0].n, 0); await assert.rejects(CustodyMarket.open(dir, options), /frozen/); await f.admin.query('DROP TRIGGER fail_import ON wk_custody.receipts'); await importSqliteCustody(file, f.admin); const restored = await PostgresCustody.open(f.runtime, options); try { assert.equal((await restored.audit()).assetsAtomicXmr, '1000000000000'); } finally { await restored.close(); } }); test('business audit rejects metadata corruption even when every journal and total remains balanced', async t => { const f = await fixture(t); await f.execute(buyer, { type: 'mock-credit' }); const l = await f.execute(seller, { type: 'list', cardHash: hash, sequence: 0, priceAtomicXmr: '10000', feeBps: 250 }) as CustodyListing; const o = await f.execute(buyer, { type: 'reserve', listingId: l.id, priceAtomicXmr: l.priceAtomicXmr, feeBps: l.feeBps }) as CustodyOrder; const w = await f.execute(buyer, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '500', maxFeeAtomicXmr: '20' }) as CustodyWithdrawal; const probes: [string, unknown[], string][] = [ ["UPDATE wk_custody.orders SET data=jsonb_set(data,'{buyer}',to_jsonb($1::text)) WHERE id=$2", [other.actor, o.id], 'order-terms-mismatch'], ["UPDATE wk_custody.orders SET data=jsonb_set(data,'{sellerAtomicXmr}','\"1\"') WHERE id=$1", [o.id], 'order-allocation-mismatch'], ["UPDATE wk_custody.orders SET data=jsonb_set(data,'{grossAtomicXmr}','\"1.0\"') WHERE id=$1", [o.id], 'order-record-invalid'], ["UPDATE wk_custody.orders SET status='settled',data=jsonb_set(data,'{status}','\"settled\"') WHERE id=$1", [o.id], 'order-reservation-mismatch'], ["DELETE FROM wk_custody.outbox WHERE id=$1", [o.id], 'order-state-mismatch'], ["UPDATE wk_custody.outbox SET data='{}' WHERE id=$1", [o.id], 'order-outbox-invalid'], ["UPDATE wk_custody.listings SET data=jsonb_set(data,'{feeBps}','500') WHERE id=$1", [l.id], 'order-allocation-mismatch'], ["UPDATE wk_custody.withdrawals SET data=jsonb_set(data,'{maxFeeAtomicXmr}','\"21\"') WHERE id=$1", [w.id], 'withdrawal-reservation-mismatch'], ["UPDATE wk_custody.withdrawals SET data=jsonb_set(data,'{actor}',to_jsonb($1::text)) WHERE id=$2", [other.actor, w.id], 'withdrawal-identity-mismatch'], ["UPDATE wk_custody.outbox SET status='fixture-prepared' WHERE id=$1", [w.id], 'withdrawal-outbox-mismatch'], ["INSERT INTO wk_custody.outbox VALUES($1,'withdrawal','blocked','{}')", [randomUUID()], 'orphan-outbox'], ["INSERT INTO wk_custody.accounts(id,normal) VALUES($1,'credit')", [`customer:${buyer.actor}:purchase:${randomUUID()}`], 'orphan-purchase-reserve'], ]; const c = await f.admin.connect(); try { for (const [sql, args, code] of probes) { await c.query('BEGIN'); try { await c.query(sql, args); const totals = await c.query("SELECT sum(CASE WHEN normal='debit' THEN balance ELSE -balance END)::text AS net FROM wk_custody.accounts"); assert.equal(totals.rows[0].net, '0'); // This would pass the old aggregate-only accounting audit. await assert.rejects(auditPostgres(c), (e: unknown) => e instanceof CustodyIntegrityError && e.code === code, code); } finally { await c.query('ROLLBACK'); } } } finally { c.release(); } assert.equal((await f.market.audit()).journals, 3); }); test('integrity incident blocks new mutations across instances/restarts but preserves private reads and original receipts', async t => { const f = await fixture(t), second = await PostgresCustody.open(f.runtime, options); const credit = envelope(f.market.id, buyer, { type: 'mock-credit' }); await f.market.execute(credit); const w = await f.execute(buyer, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '100', maxFeeAtomicXmr: '0' }) as CustodyWithdrawal; try { await f.admin.query("UPDATE wk_custody.withdrawals SET data=jsonb_set(data,'{amountAtomicXmr}','\"101\"') WHERE id=$1", [w.id]); await assert.rejects(f.market.audit(), (e: unknown) => e instanceof CustodyIntegrityError && e.code === 'withdrawal-reservation-mismatch'); const status = f.market.diagnostics(); assert.equal(status.integrityHold, true); assert.equal(status.mutationHold, true); const text = JSON.stringify(status); for (const secret of [buyer.actor, w.id, 'postgresql:', 'password']) assert.equal(text.includes(secret), false); const marker = await f.admin.query("SELECT value FROM wk_custody.custody_meta WHERE key='integrity_incident_v1'"); assert.equal(JSON.parse(marker.rows[0].value).code, 'withdrawal-reservation-mismatch'); await assert.rejects(second.execute(envelope(second.id, other, { type: 'mock-credit' })), /integrity incident/); assert.equal((await f.market.execute(credit) as CustodyActivity).availableAtomicXmr, '1000000000000'); assert.equal((await f.activity()).withdrawalReservedAtomicXmr, '100'); await assert.rejects(PostgresCustody.open(f.runtime, options), /unresolved-integrity-incident/); // Restore this disposable corruption to prove that repair alone does not silently clear the incident. await f.admin.query('UPDATE wk_custody.withdrawals SET data=$1 WHERE id=$2', [JSON.stringify(w), w.id]); await assert.rejects(f.market.audit(), /unresolved-integrity-incident/); assert.equal((await f.activity()).availableAtomicXmr, '999999999900'); const runtimePool = new Pool(f.runtime); try { await assert.rejects(resolveCustodyIncident(runtimePool, randomUUID()), /Schema-owner/); } finally { await runtimePool.end(); } const resolved = await resolveCustodyIncident(f.admin, randomUUID()); assert.ok(resolved.resolutionId); // Previously detecting instances remain latched even after authorized acknowledgement. await f.market.audit(); assert.equal(f.market.diagnostics().integrityHold, true); await assert.rejects(f.execute(other, { type: 'mock-credit' }), /reconciliation/); const verified = await PostgresCustody.open(f.runtime, options); try { assert.equal(verified.diagnostics().mutationHold, false); await verified.audit(); } finally { await verified.close(); } } finally { await second.close(); } }); test('targeted mutation checks reject corrupt reservations before posting or cancelling their outbox', async t => { const f = await fixture(t); await f.execute(buyer, { type: 'mock-credit' }); const w = await f.execute(buyer, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '500', maxFeeAtomicXmr: '20' }) as CustodyWithdrawal; await f.admin.query("UPDATE wk_custody.outbox SET data='{}' WHERE id=$1", [w.id]); await assert.rejects(f.execute(buyer, { type: 'cancel-withdrawal', withdrawalId: w.id }), /integrity check/); assert.equal((await f.admin.query('SELECT status FROM wk_custody.withdrawals WHERE id=$1', [w.id])).rows[0].status, 'reserved'); assert.equal((await f.admin.query("SELECT count(*)::int AS n FROM wk_custody.journals WHERE kind='withdrawal-cancellation'")).rows[0].n, 0); assert.equal((await f.activity()).availableAtomicXmr, '999999999480'); assert.equal(f.market.diagnostics().integrityHold, true); await assert.rejects(resolveCustodyIncident(f.admin, randomUUID()), /integrity check/); assert.equal((await f.admin.query("SELECT count(*)::int AS n FROM wk_custody.custody_meta WHERE key='integrity_incident_v1'")).rows[0].n, 1); }); test('business audit pages past 128 records and catches later corrupted output without dropping claims', async t => { const f = await fixture(t); await f.execute(buyer, { type: 'mock-credit' }); let last: CustodyWithdrawal | undefined; for (let i = 0; i < 130; i++) { const w = await f.execute(buyer, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '1', maxFeeAtomicXmr: '0' }) as CustodyWithdrawal; if (!last || last.id < w.id) last = w; } assert.equal((await f.market.audit()).journals, 131); await f.admin.query("UPDATE wk_custody.outbox SET data='{}' WHERE id=$1", [last!.id]); await assert.rejects(f.market.audit(), /withdrawal-outbox-invalid/); assert.equal((await f.activity()).withdrawalReservedAtomicXmr, '130'); }); test('imported fixture completion and cancellation states reconcile their exact terminal postings', async t => { const f = await fixture(t); await f.admin.query('DELETE FROM wk_custody.custody_meta'); const dir = await mkdtemp(join(tmpdir(), 'custody-terminal-audit-')); t.after(() => rm(dir, { recursive: true, force: true })); const source = await CustodyMarket.open(dir, options), id = source.id; const run = (who: typeof buyer, action: CustodyAction) => source.execute(envelope(id, who, action)); run(buyer, { type: 'mock-credit' }); const l = run(seller, { type: 'list', cardHash: hash, sequence: 0, priceAtomicXmr: '1000', feeBps: 250 }) as CustodyListing; const o = run(buyer, { type: 'reserve', listingId: l.id, priceAtomicXmr: l.priceAtomicXmr, feeBps: 250 }) as CustodyOrder; source.settleFixture(o.id, `fixture:${'c'.repeat(64)}`); const w = run(buyer, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '1000', maxFeeAtomicXmr: '50' }) as CustodyWithdrawal; source.prepareWithdrawalFixture(w.id, randomUUID(), `fixture:${'d'.repeat(64)}`, '10'); source.confirmWithdrawalFixture(w.id, `fixture:${'d'.repeat(64)}`); const cancelled = run(buyer, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '100', maxFeeAtomicXmr: '5' }) as CustodyWithdrawal; run(buyer, { type: 'cancel-withdrawal', withdrawalId: cancelled.id }); const expected = source.audit(); source.close(); await importSqliteCustody(join(dir, 'testnet', 'custody-sandbox.sqlite'), f.admin); const pg = await PostgresCustody.open(f.runtime, options); try { assert.deepEqual(await pg.audit(), expected); } finally { await pg.close(); } }); test('sale rehearsal requires explicit disposable instance consent and exact trader-signed reservation terms', async t => { const f = await fixture(t); await f.execute(buyer, { type: 'mock-credit' }); await assert.rejects(PostgresCustody.open({ ...f.runtime, database: 'postgres' }, { ...options, disposableSaleRehearsal: true }), /disposable database/); const l = await f.execute(seller, { type: 'list', cardHash: hash, sequence: 0, priceAtomicXmr: '100000000000', feeBps: 250 }) as CustodyListing; const o = await f.execute(buyer, { type: 'reserve', listingId: l.id, priceAtomicXmr: l.priceAtomicXmr, feeBps: 250 }) as CustodyOrder; const t1 = saleTranscript(o, f.market.id); assert.throws(() => f.market.observeSaleRehearsal(o.id, t1.input), /unavailable/); await assert.rejects(f.market.observeCanonicalEvents({}), /unavailable/); const rehearsal = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); try { const wrong = structuredClone(t1.input); wrong.lock = { ...t1.input.lock, terms: { ...t1.input.lock.terms, priceAtomicXmr: '1' } } as typeof wrong.lock; await assert.rejects(rehearsal.observeSaleRehearsal(o.id, wrong), /operation failed/); const locked = await rehearsal.observeSaleRehearsal(o.id, t1.input); assert.equal(locked.action, 'hold'); assert.equal(locked.reservationDigest, t1.reservationDigest); await assert.rejects(rehearsal.observeSaleRehearsal(o.id, { ...t1.input, seed: { ...t1.input.seed, pageId: 2 } }), /authorization changed/); await assert.rejects(rehearsal.observeSaleRehearsal(o.id, { ...t1.input, blocks: Array(129).fill([]) }), /operation failed/); const cycle: Record = {}; cycle.self = cycle; await assert.rejects(rehearsal.observeSaleRehearsal(o.id, cycle), /operation failed/); await assert.rejects(f.execute(buyer, { type: 'cancel-reservation', orderId: o.id }), /authorization exists/); assert.equal((await f.activity()).purchaseReservedAtomicXmr, o.grossAtomicXmr); assert.equal((await f.activity(seller)).availableAtomicXmr, '0'); assert.equal(rehearsal.info().nftSettlement, false); await rehearsal.audit(); } finally { await rehearsal.close(); } }); test('canonical connect/disconnect inbox resumes bounded pages, retries idempotently, and audits after reconnect', async t => { const f = await fixture(t); const observer = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); const streamId = randomUUID(), events = canonicalEvents(streamId); const first = canonicalPage(streamId, events, 0, 2), second = canonicalPage(streamId, events, 2, 2); try { const accepted = await observer.observeCanonicalEvents(first); assert.equal(accepted.cursor, 2); assert.equal(accepted.height, 0); assert.equal(accepted.tip, GENESIS_HASH); assert.equal((await observer.observeCanonicalEvents(first)).replayed, true); // A second consumer may have fetched a larger page before the first writer advanced the inbox. const overlapping = await observer.observeCanonicalEvents(canonicalPage(streamId, events, 0, 4)); assert.equal(overlapping.cursor, 4); assert.equal(overlapping.replayed, false); const stale = await observer.observeCanonicalEvents({ ...first, head: 2, caughtUp: true }); assert.equal(stale.cursor, 4); assert.equal(stale.head, 4); assert.equal(stale.replayed, true); const tip = await observer.observeCanonicalEvents(second); assert.equal(tip.cursor, 4); assert.equal(tip.head, 4); assert.equal(tip.height, 2); assert.equal(tip.tip, 'c'.repeat(64)); assert.equal(tip.replayed, true); assert.equal((await f.admin.query("SELECT count(*)::int AS n FROM wk_custody.custody_meta WHERE key LIKE 'canonical-inbox:v1:event:%'")).rows[0].n, 4); await observer.audit(); } finally { await observer.close(); } const reopened = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); try { const replay = await reopened.observeCanonicalEvents(second); assert.equal(replay.cursor, 4); assert.equal(replay.height, 2); assert.equal(replay.replayed, true); await reopened.audit(); } finally { await reopened.close(); } }); test('gated adapter consumes actual validated local Ledger connect and reorg pages', async t => { const dir = await mkdtemp(join(tmpdir(), 'wikideck-pg-local-ledger-')); const pow = new FixturePow(), ledger = await Ledger.open(dir, pow), f = await fixture(t); const observer = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); t.after(async () => { await observer.close(); ledger.close(); pow.close(); await rm(dir, { recursive: true, force: true }); }); const canonical = await fixtureBlock([], {}); await ledger.submit(canonical); const first = await observer.observeLedgerCanonicalEvents(ledger); assert.equal(first.cursor, 1); assert.equal(first.height, 1); assert.equal(first.tip, canonical.hash); const side = await fixtureBlock([], {}, 50001); const sideResult = await ledger.submit(side); // Equal-work branch remains noncanonical and emits no event. assert.equal(sideResult.accepted, true); assert.equal(ledger.canonicalHead(), 1); const sideChain = [side]; let sideState = transition({}, side, []), sideChild = side; let reorgResult = { accepted: true, reorg: false }; for (let i = 0; i < 16 && !reorgResult.reorg; i++) { sideChild = await fixtureBlock(sideChain, sideState, 50002 + i); reorgResult = await ledger.submit(sideChild); sideState = transition(sideState, sideChild, []); sideChain.push(sideChild); } assert.equal(reorgResult.reorg, true, `Expected longer-work side branch to win; events=${JSON.stringify(ledger.canonicalEvents(0))}`); const expectedCursor = sideChain.length + 2; // initial connect, old-tip disconnect, then each side-branch connect assert.equal(ledger.canonicalHead(), expectedCursor); const reorg = await observer.observeLedgerCanonicalEvents(ledger); assert.equal(reorg.cursor, expectedCursor); assert.equal(reorg.height, sideChain.length); assert.equal(reorg.tip, sideChild.hash); assert.equal((await f.admin.query("SELECT count(*)::int AS n FROM wk_custody.custody_meta WHERE key LIKE 'canonical-inbox:v1:event:%'")).rows[0].n, expectedCursor); await observer.audit(); }); test('observer consumes a block independently validated with the native RandomX engine', { timeout: 120000 }, async t => { const binary = resolve(process.env.RANDOMX_BINARY ?? join(process.cwd(), 'native', 'build', 'wikideck-randomx')); try { await access(binary, constants.X_OK); } catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; t.skip(`Native RandomX binary absent: ${binary}`); return; } const dir = await mkdtemp(join(tmpdir(), 'wikideck-pg-real-randomx-')); const engine = new RandomX({ binary }), ledger = await Ledger.open(dir, engine), f = await fixture(t); const observer = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); t.after(async () => { await observer.close(); ledger.close(); engine.close(); await rm(dir, { recursive: true, force: true }); }); const template = makeTemplate([], {}, seller.actor, seller.publicKey, [], fixtureArticle(55000), fixtureStats(), GENESIS_TIME + 30); const header = structuredClone(template.header); let mined: Awaited> = null; for (let batch = 0; batch < 16 && !mined; batch++) { mined = await engine.mine(header.key, miningBlob(header), header.target, 32, 64); if (!mined) header.nonce += 64; } assert.ok(mined, 'Native RandomX fixture mining exceeded bounded attempts'); header.nonce = mined.nonce; const blockHash = sha256(miningBlob(header)); const block: TestnetBlock = { ...header, hash: blockHash, tokenId: mintId(header), powHash: mined.hash, signature: seller.sign(blockClaimMessage(blockHash)), transactions: [] }; await ledger.submit(block); // Ledger independently recalculates and validates native RandomX. const page = await observer.observeLedgerCanonicalEvents(ledger); assert.equal(page.cursor, 1); assert.equal(page.height, 1); assert.equal(page.tip, block.hash); await observer.audit(); }); test('scheduled observer bounds catch-up, stops cleanly, and resumes its durable cursor after restart', async t => { const dir = await mkdtemp(join(tmpdir(), 'wikideck-pg-observer-runner-')); const pow = new EasyFixturePow(), ledger = await Ledger.open(dir, pow), f = await fixture(t); const observer = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); t.after(async () => { await observer.close(); ledger.close(); pow.close(); await rm(dir, { recursive: true, force: true }); }); const chain: TestnetBlock[] = []; let state: NftStateView = {}; for (let i = 0; i < 66; i++) { const block = await fixtureBlock(chain, state, 51000 + i, pow); await ledger.submit(block); state = transition(state, block, []); chain.push(block); } const firstRunner = new CanonicalObserver(observer, ledger, { intervalMs: 60000, pagesPerCycle: 1 }); try { const priorMode = process.env.NODE_ENV, priorGate = process.env.WIKIDECK_CUSTODY_CANONICAL_REHEARSAL; try { process.env.NODE_ENV = 'production'; assert.throws(() => new CanonicalObserver(observer, ledger), /non-production/); process.env.NODE_ENV = 'test'; delete process.env.WIKIDECK_CUSTODY_CANONICAL_REHEARSAL; assert.throws(() => new CanonicalObserver(observer, ledger), /non-production/); } finally { if (priorMode === undefined) delete process.env.NODE_ENV; else process.env.NODE_ENV = priorMode; if (priorGate === undefined) delete process.env.WIKIDECK_CUSTODY_CANONICAL_REHEARSAL; else process.env.WIKIDECK_CUSTODY_CANONICAL_REHEARSAL = priorGate; } const first = await firstRunner.start(); assert.equal(first.state, 'running'); assert.equal(first.cursor, 64); assert.equal(first.head, 66); assert.equal(first.caughtUp, false); const caughtUp = await firstRunner.pollNow(); assert.equal(caughtUp.cursor, 66); assert.equal(caughtUp.caughtUp, true); assert.equal(caughtUp.tip, chain.at(-1)!.hash); await firstRunner.close(); assert.equal(firstRunner.status().state, 'stopped'); const next = await fixtureBlock(chain, state, 52000, pow); await ledger.submit(next); assert.equal((await f.admin.query("SELECT count(*)::int AS n FROM wk_custody.custody_meta WHERE key LIKE 'canonical-inbox:v1:event:%'")).rows[0].n, 66); await assert.rejects(firstRunner.pollNow(), /not running/); const restarted = new CanonicalObserver(observer, ledger, { intervalMs: 60000, pagesPerCycle: 1 }); try { const resumed = await restarted.start(); assert.equal(resumed.cursor, 67); assert.equal(resumed.head, 67); assert.equal(resumed.caughtUp, true); assert.equal(resumed.tip, next.hash); } finally { await restarted.close(); } assert.equal((await f.admin.query("SELECT count(*)::int AS n FROM wk_custody.custody_meta WHERE key LIKE 'canonical-inbox:v1:event:%'")).rows[0].n, 67); chain.push(next); state = transition(state, next, []); const automatic = new CanonicalObserver(observer, ledger, { intervalMs: 25, pagesPerCycle: 1 }); try { assert.equal((await automatic.start()).cursor, 67); const automaticBlock = await fixtureBlock(chain, state, 52001, pow); await ledger.submit(automaticBlock); const deadline = Date.now() + 3000; while (automatic.status().cursor < 68 && Date.now() < deadline) await new Promise(resolve => setTimeout(resolve, 10)); assert.equal(automatic.status().cursor, 68, 'scheduled poll did not consume the new ledger event'); } finally { await automatic.close(); } } finally { await firstRunner.close(); } }); test('canonical inbox rejects a disconnected block with a forged but rehashed ancestor', async t => { const f = await fixture(t), observer = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); const streamId = randomUUID(), events = canonicalEvents(streamId); try { events[1].previousHash = '9'.repeat(64); reanchorCanonicalEvents(streamId, events); await assert.rejects(observer.observeCanonicalEvents(canonicalPage(streamId, events, 0, 2)), /disconnect-ancestry-mismatch/); assert.equal((await f.admin.query("SELECT count(*)::int AS n FROM wk_custody.custody_meta WHERE key LIKE 'canonical-inbox:v1:event:%'")).rows[0].n, 0); await assert.rejects(PostgresCustody.open(f.runtime, options), /unresolved-integrity/); } finally { await observer.close(); } }); test('canonical inbox rejects tampered event anchors, persists an integrity hold and refuses another stream', async t => { const f = await fixture(t), observer = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); const streamId = randomUUID(), events = canonicalEvents(streamId); try { const tampered = canonicalPage(streamId, events, 0, 2); tampered.events[0].transactionIds = ['f'.repeat(64)]; await assert.rejects(observer.observeCanonicalEvents(tampered), /canonical-inbox-event-hash-invalid/); assert.equal((await f.admin.query("SELECT count(*)::int AS n FROM wk_custody.custody_meta WHERE key LIKE 'canonical-inbox:v1:event:%'")).rows[0].n, 0); await assert.rejects(PostgresCustody.open(f.runtime, options), /unresolved-integrity/); } finally { await observer.close(); } }); test('canonical inbox startup audit detects owner-side history mutation and durably fences writers', async t => { const f = await fixture(t), observer = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); const streamId = randomUUID(), events = canonicalEvents(streamId); try { await observer.observeCanonicalEvents(canonicalPage(streamId, events, 0, 2)); const changed = { ...events[0], transactionIds: ['f'.repeat(64)] }; await f.admin.query('UPDATE wk_custody.custody_meta SET value=$1 WHERE key=$2', [JSON.stringify(changed), `canonical-inbox:v1:event:${'1'.padStart(16, '0')}`]); await assert.rejects(observer.audit(), /canonical-inbox-event-anchor-invalid/); assert.equal((await f.admin.query("SELECT count(*)::int AS n FROM wk_custody.custody_meta WHERE key='integrity_incident_v1'")).rows[0].n, 1); await assert.rejects(PostgresCustody.open(f.runtime, options), /unresolved-integrity/); } finally { await observer.close(); } }); test('signed candidate completion settles actual PostgreSQL balances once across pools and reconnect', async t => { const f = await fixture(t); await f.execute(buyer, { type: 'mock-credit' }); const l = await f.execute(seller, { type: 'list', cardHash: hash, sequence: 0, priceAtomicXmr: '100000000000', feeBps: 250 }) as CustodyListing; const o = await f.execute(buyer, { type: 'reserve', listingId: l.id, priceAtomicXmr: l.priceAtomicXmr, feeBps: 250 }) as CustodyOrder; const transcript = saleTranscript(o, f.market.id); const a = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); const b = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); const mature = { ...transcript.input, blocks: [[transcript.input.lock], [], [transcript.complete], [], []] }; try { const immature = { ...mature, blocks: mature.blocks.slice(0, 3) }; assert.equal((await a.observeSaleRehearsal(o.id, immature)).action, 'hold'); const results = await Promise.all([a.observeSaleRehearsal(o.id, mature), b.observeSaleRehearsal(o.id, mature)]); assert.deepEqual(results.map(r => r.action).sort(), ['none', 'settle']); assert.equal((await f.activity(seller)).availableAtomicXmr, '96500000000'); assert.equal((await f.activity()).purchaseReservedAtomicXmr, '0'); const claims = await f.admin.query("SELECT id,balance::text FROM wk_custody.accounts WHERE id LIKE 'author:%' OR id LIKE 'revenue:host:%'"); assert.deepEqual(claims.rows.map(r => r.balance).sort(), ['1000000000', '2500000000']); assert.equal((await a.audit()).journals, 3); const reopened = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); try { assert.equal((await reopened.observeSaleRehearsal(o.id, mature)).action, 'none'); await reopened.audit(); } finally { await reopened.close(); } // Fork replaces completion with valid cancellation. Financial history stays intact and incident is durable. const fork = { ...mature, blocks: [[transcript.input.lock], [], [transcript.cancel], [], []] }; await assert.rejects(a.observeSaleRehearsal(o.id, fork), /outcome-reorg/); await assert.rejects(b.observeSaleRehearsal(o.id, mature), /incident unresolved/); assert.equal((await f.activity(seller)).availableAtomicXmr, '96500000000'); assert.equal((await f.admin.query("SELECT count(*)::int AS n FROM wk_custody.journals WHERE kind='mock-sale-settlement'")).rows[0].n, 1); await assert.rejects(PostgresCustody.open(f.runtime, options), /unresolved-integrity/); } finally { await a.close(); await b.close(); } }); test('disconnect of the exact posted candidate terminal transaction commits an order-linked review hold', async t => { const f = await fixture(t); await f.execute(buyer, { type: 'mock-credit' }); const l = await f.execute(seller, { type: 'list', cardHash: hash, sequence: 0, priceAtomicXmr: '100000000000', feeBps: 250 }) as CustodyListing; const o = await f.execute(buyer, { type: 'reserve', listingId: l.id, priceAtomicXmr: l.priceAtomicXmr, feeBps: 250 }) as CustodyOrder; const s = saleTranscript(o, f.market.id), transcript = { ...s.input, blocks: [[s.input.lock], [], [s.complete], [], []] }; const terminalTxId = saleCandidateTransactionId(s.complete); const rehearsal = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); const observer = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); const streamId = randomUUID(), events = canonicalEvents(streamId, terminalTxId); try { assert.equal((await rehearsal.observeSaleRehearsal(o.id, transcript)).action, 'settle'); const credited = await f.activity(seller); assert.equal(credited.availableAtomicXmr, '96500000000'); const first = await observer.observeCanonicalEvents(canonicalPage(streamId, events, 0, 1)); assert.equal(first.cursor, 1); // The same transaction reappears on the replacement branch in this page; still review, never auto-clear. const review = await observer.observeCanonicalEvents(canonicalPage(streamId, events, 1, 2)); assert.equal(review.reviewRequired, true); assert.equal(review.cursor, 3); // Disconnect and reappearance are durably retained with the review marker. await assert.rejects(observer.observeCanonicalEvents(canonicalPage(streamId, events, 1, 2)), /hold|incident unresolved/); assert.equal((await f.admin.query("SELECT count(*)::int AS n FROM wk_custody.custody_meta WHERE key LIKE 'canonical-inbox:v1:event:%'")).rows[0].n, 3); assert.equal((await f.activity(seller)).availableAtomicXmr, credited.availableAtomicXmr); assert.equal((await f.admin.query("SELECT count(*)::int AS n FROM wk_custody.journals WHERE id=$1", [`settle:${o.id}`])).rows[0].n, 1); const incident = await f.admin.query("SELECT value FROM wk_custody.custody_meta WHERE key='integrity_incident_v1'"); assert.match(incident.rows[0].value, /posted-sale-terminal-disconnected/); await assert.rejects(observer.audit(), /unresolved-integrity-incident/); await assert.rejects(resolveCustodyIncident(f.admin, randomUUID()), /posted-sale-terminal-disconnected/); assert.equal((await f.admin.query("SELECT count(*)::int AS n FROM wk_custody.custody_meta WHERE key LIKE 'sale-reorg-review:%'")).rows[0].n, 1); await assert.rejects(PostgresCustody.open(f.runtime, options), /unresolved-integrity/); await assert.rejects(f.execute(other, { type: 'mock-credit' }), /incident is unresolved/); } finally { await observer.close(); await rehearsal.close(); } }); test('candidate cancellation and withheld-lock expiry release actual reservations only after depth', async t => { const f = await fixture(t); await f.execute(buyer, { type: 'mock-credit' }); const rehearsal = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); try { for (const withheld of [false, true]) { const l = await f.execute(seller, { type: 'list', cardHash: hash, sequence: 0, priceAtomicXmr: '1000', feeBps: 250 }) as CustodyListing; const o = await f.execute(buyer, { type: 'reserve', listingId: l.id, priceAtomicXmr: l.priceAtomicXmr, feeBps: 250 }) as CustodyOrder; const s = saleTranscript(o, f.market.id); const transcript = { ...s.input, blocks: withheld ? [[], [], [], [], []] : [[s.input.lock], [s.cancel], []] }; assert.equal((await rehearsal.observeSaleRehearsal(o.id, transcript)).action, 'hold'); transcript.blocks.push([]); assert.equal((await rehearsal.observeSaleRehearsal(o.id, transcript)).action, 'refund'); assert.equal((await rehearsal.observeSaleRehearsal(o.id, transcript)).action, 'none'); assert.equal((await f.activity()).availableAtomicXmr, '1000000000000'); await rehearsal.audit(); } } finally { await rehearsal.close(); } }); test('rehearsal rejects a different fee rate even when tiny-order commission rounding is identical', async t => { const f = await fixture(t); await f.execute(buyer, { type: 'mock-credit' }); const l = await f.execute(seller, { type: 'list', cardHash: hash, sequence: 0, priceAtomicXmr: '1000', feeBps: 250 }) as CustodyListing; const o = await f.execute(buyer, { type: 'reserve', listingId: l.id, priceAtomicXmr: l.priceAtomicXmr, feeBps: 250 }) as CustodyOrder; const s = saleTranscript(o, f.market.id, 251); assert.throws(() => replayReservedSale(o, f.market.id, s.input, l.feeBps), /fee rate/); const rehearsal = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); try { await assert.rejects(rehearsal.observeSaleRehearsal(o.id, s.input), /operation failed/); assert.equal((await f.activity()).purchaseReservedAtomicXmr, '1000'); assert.equal((await f.admin.query("SELECT count(*)::int AS n FROM wk_custody.custody_meta WHERE key LIKE 'sale-rehearsal-%'")).rows[0].n, 0); } finally { await rehearsal.close(); } }); test('rehearsal preserves one-atomic sales and the signed custom host recipient across policy changes', async t => { const f = await fixture(t); await f.execute(buyer, { type: 'mock-credit' }); const recipient = await syntheticFeeAddress(); const custom = await PostgresCustody.open(f.runtime, { ...options, hostFeeAddress: recipient }); const rehearsal = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); try { for (const price of ['1', '1000']) { const l = await custom.execute(envelope(custom.id, seller, { type: 'list', cardHash: hash, sequence: 0, priceAtomicXmr: price, feeBps: 250, hostFeeAddress: recipient })) as CustodyListing; const o = await f.execute(buyer, { type: 'reserve', listingId: l.id, priceAtomicXmr: price, feeBps: 250, hostFeeAddress: recipient }) as CustodyOrder; const s = saleTranscript(o, f.market.id); const changed = structuredClone(s.input); changed.lock.terms.hostFeeAddress = f.market.info().hostFeeAddress; await assert.rejects(rehearsal.observeSaleRehearsal(o.id, changed), /operation failed/); const transcript = { ...s.input, blocks: [[s.input.lock], [], [s.complete], [], []] }; assert.equal((await rehearsal.observeSaleRehearsal(o.id, transcript)).action, 'settle'); assert.equal((await rehearsal.observeSaleRehearsal(o.id, transcript)).action, 'none'); await rehearsal.audit(); } assert.equal((await f.activity(seller)).availableAtomicXmr, '966'); assert.equal((await f.admin.query('SELECT balance::text FROM wk_custody.accounts WHERE id=$1', [`revenue:host:${recipient}`])).rows[0].balance, '25'); assert.equal((await f.admin.query("SELECT count(*)::int AS n FROM wk_custody.custody_meta WHERE key LIKE 'sale-rehearsal-observation:%'")).rows[0].n, 2); } finally { await custom.close(); await rehearsal.close(); } }); test('sale terminal-posting evidence failure rolls back seller credit and remains safely retryable', async t => { const f = await fixture(t); await f.execute(buyer, { type: 'mock-credit' }); const l = await f.execute(seller, { type: 'list', cardHash: hash, sequence: 0, priceAtomicXmr: '1000', feeBps: 250 }) as CustodyListing; const o = await f.execute(buyer, { type: 'reserve', listingId: l.id, priceAtomicXmr: l.priceAtomicXmr, feeBps: 250 }) as CustodyOrder; const s = saleTranscript(o, f.market.id), transcript = { ...s.input, blocks: [[s.input.lock], [], [s.complete], [], []] }; const rehearsal = await PostgresCustody.open(f.runtime, { ...options, disposableSaleRehearsal: true }); try { await f.admin.query("CREATE FUNCTION wk_custody.fail_terminal() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN IF NEW.key LIKE 'sale-rehearsal-terminal:%' THEN RAISE EXCEPTION 'fixture terminal failure'; END IF; RETURN NEW; END $$; CREATE TRIGGER fail_terminal BEFORE INSERT ON wk_custody.custody_meta FOR EACH ROW EXECUTE FUNCTION wk_custody.fail_terminal()"); await assert.rejects(rehearsal.observeSaleRehearsal(o.id, transcript), /operation failed/); assert.equal((await f.activity(seller)).availableAtomicXmr, '0'); assert.equal((await f.activity()).purchaseReservedAtomicXmr, '1000'); await f.admin.query('DROP TRIGGER fail_terminal ON wk_custody.custody_meta'); assert.equal((await rehearsal.observeSaleRehearsal(o.id, transcript)).action, 'settle'); await rehearsal.audit(); await f.admin.query("UPDATE wk_custody.custody_meta SET value='{}' WHERE key=$1", [`sale-rehearsal-terminal:${o.id}`]); await assert.rejects(rehearsal.audit(), /evidence-mismatch/); } finally { await rehearsal.close(); } });