Durable, beneficiary-bound subaddress index reservation

This commit is contained in:
2026-10-22 17:26:13 -04:00
parent 84e6e08209
commit 2ecb42c019
9 changed files with 416 additions and 42 deletions
+91 -5
View File
@@ -15,10 +15,12 @@ import { CustodyIntegrityError, auditBusinessLiabilities } from '../../server/ma
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, CUSTODY_MIGRATION_V2,
grantMoneroDepositCreditRuntime, grantMoneroDepositAllocatorRuntime, grantMoneroWithdrawalRuntime,
grantPilotDepositReservationRuntime, 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 { reservePilotMoneroDepositRequest } from '../../server/market/pilot-monero-deposit-allocation.ts';
import { readMainnetCustodyBalance, reserveMainnetWithdrawal, cancelMainnetWithdrawal } from '../../server/market/mainnet-withdrawal.ts';
import { mainnetCustodyBalanceMessage, mainnetWithdrawalCancellationMessage, mainnetWithdrawalMessage,
type MainnetCustodyBalanceRequestUnsigned, type MainnetWithdrawalRequestUnsigned,
@@ -206,7 +208,7 @@ test('explicit PostgreSQL migrations are idempotent and runtime role cannot alte
assert.equal((await f.market.audit()).assetsAtomicXmr, '1000000000000');
});
test('custody schema v1 upgrades additively through v10 without dropping existing metadata', async t => {
test('custody schema v1 upgrades additively through v11 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();
@@ -220,7 +222,7 @@ test('custody schema v1 upgrades additively through v10 without dropping existin
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]);
assert.deepEqual(versions.rows.map(row => Number(row.version)), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
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']) {
@@ -228,6 +230,7 @@ test('custody schema v1 upgrades additively through v10 without dropping existin
}
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);
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);
@@ -235,7 +238,7 @@ test('custody schema v1 upgrades additively through v10 without dropping existin
} finally { await admin.end(); }
});
test('custody schema v2 upgrades explicitly through v10 deposit and withdrawal lifecycle tables', async t => {
test('custody schema v2 upgrades explicitly through v11 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();
@@ -248,11 +251,12 @@ test('custody schema v2 upgrades explicitly through v10 deposit and withdrawal l
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]);
assert.deepEqual(versions.rows.map(row => Number(row.version)), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
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(); }
});
@@ -324,6 +328,88 @@ test('pilot custody identity refuses a database containing pre-existing mock or
} 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('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/);