Validation-only pilot contract

This commit is contained in:
2026-10-22 16:11:36 -04:00
parent 359f0df32d
commit 84e6e08209
5 changed files with 182 additions and 3 deletions
+66
View File
@@ -0,0 +1,66 @@
import { createHash } from 'node:crypto';
import { IS_PILOT, NETWORK } from '../../shared/network.ts';
import { pilotMoneroDepositRequestMessage, pilotMoneroDepositRequestSchema } from '../../shared/pilot-monero-deposit.ts';
import type { PilotCustodyIdentity } from './pilot-custody-identity.ts';
import { address, CHAIN_ID, verifySignature } from '../testnet/consensus.ts';
const MAX_REQUEST_LIFETIME_MS = 5 * 60_000;
export type PilotDepositRequestContext = {
identity: PilotCustodyIdentity;
walletGenerationId: string;
walletFingerprint: string;
accountIndex: number;
now?: () => number;
};
/**
* Validate a signed pilot deposit intent without allocating an address or touching PostgreSQL/Wallet-RPC.
* This boundary is intentionally identity-only until credit, withdrawal, and recovery are connected.
*/
export function validatePilotMoneroDepositRequest(context: PilotDepositRequestContext, rawRequest: unknown) {
if (!IS_PILOT || NETWORK !== 'wikideck-randomx-pilot-1') {
throw new Error('Pilot Monero deposit requests require the pilot market profile');
}
const request = pilotMoneroDepositRequestSchema.parse(rawRequest);
const { identity } = context;
if (identity.version !== 1 || identity.mode !== 'pilot-custody' || identity.policy !== 'pilot-custody-v1' ||
identity.marketNetwork !== NETWORK || identity.marketChainId !== CHAIN_ID ||
identity.moneroNetwork !== 'mainnet' || identity.activationStatus !== 'identity-only' ||
request.marketNetwork !== identity.marketNetwork || request.marketChainId !== identity.marketChainId ||
request.moneroNetwork !== identity.moneroNetwork || request.custodianId !== identity.custodianId ||
request.walletGenerationId !== context.walletGenerationId || request.walletFingerprint !== context.walletFingerprint ||
request.accountIndex !== context.accountIndex) {
throw new Error('Pilot deposit request does not match the identity, Monero network, or wallet generation');
}
if (!/^[a-f0-9]{64}$/.test(context.walletFingerprint) ||
!Number.isSafeInteger(context.accountIndex) || context.accountIndex < 0 || context.accountIndex > 0xffff_ffff) {
throw new Error('Invalid configured pilot watch-only wallet identity');
}
if (address(request.publicKey) !== request.beneficiary) {
throw new Error('Pilot deposit request signer does not own the requested beneficiary');
}
const { signature, ...unsigned } = request;
const message = pilotMoneroDepositRequestMessage(unsigned);
if (!verifySignature(request.publicKey, request.beneficiary, message, signature)) {
throw new Error('Pilot deposit request signature is invalid');
}
const now = context.now?.() ?? Date.now();
if (!Number.isSafeInteger(now) || now < 0 || request.expiresAt < now || request.expiresAt > now + MAX_REQUEST_LIFETIME_MS) {
throw new Error('Pilot deposit request is expired or outside the allowed window');
}
const requestDigest = createHash('sha256').update(message).digest('hex');
return {
requestId: request.requestId,
custodianId: identity.custodianId,
marketNetwork: identity.marketNetwork,
marketChainId: identity.marketChainId,
moneroNetwork: identity.moneroNetwork,
beneficiary: request.beneficiary,
walletGenerationId: request.walletGenerationId,
walletFingerprint: request.walletFingerprint,
accountIndex: request.accountIndex,
expiresAt: request.expiresAt,
requestDigest,
} as const;
}