Strengthened the disposable reorg rehearsal

This commit is contained in:
2026-10-20 19:31:08 -04:00
parent 22da9764eb
commit 8cfcf6c025
6 changed files with 62 additions and 15 deletions
+23 -4
View File
@@ -348,27 +348,46 @@ export class PostgresCustody {
});
}
/** Disposable observer rehearsal only; input must come from Ledger.canonicalEvents(), never peers/HTTP. */
observeCanonicalEvents(page: unknown) {
async observeCanonicalEvents(page: unknown) {
check(this.options.disposableSaleRehearsal === true, 'Canonical event inbox rehearsal is unavailable on this custody instance');
return this.transaction(true, async c => {
const result = await this.transaction(true, async c => {
check(!this.diagnostics().mutationHold, 'Custody reconciliation hold');
check(!(await c.query("SELECT 1 FROM wk_custody.custody_meta WHERE key='integrity_incident_v1'")).rowCount, 'Custody integrity incident unresolved');
try {
const result = await ingestCanonicalPage(c, page);
const reviews: { orderId: string; terminalTransactionId: string; stateRoot: string; eventSequence: number; eventHash: string }[] = [];
for (const event of result.appendedEvents) {
if (event.type !== 'disconnect') continue;
for (const txid of event.transactionIds) {
const terminal = await c.query('SELECT value FROM wk_custody.custody_meta WHERE key=$1', [`sale-rehearsal-tx:${txid}`]);
if (terminal.rowCount) throw new CustodyIntegrityError('posted-sale-terminal-disconnected');
if (terminal.rowCount) {
const link = JSON.parse(terminal.rows[0].value);
if (link.version !== 1 || typeof link.orderId !== 'string' || typeof link.stateRoot !== 'string') {
throw new CustodyIntegrityError('sale-terminal-index-invalid');
}
reviews.push({ orderId: link.orderId, terminalTransactionId: txid, stateRoot: link.stateRoot,
eventSequence: event.sequence, eventHash: event.eventHash });
}
}
}
return result;
for (const review of reviews) {
await c.query('INSERT INTO wk_custody.custody_meta(key,value) VALUES($1,$2) ON CONFLICT(key) DO NOTHING',
[`sale-reorg-review:${review.orderId}:${review.eventSequence}`, JSON.stringify({ version: 1,
streamId: result.streamId, ...review })]);
}
if (reviews.length) await recordIntegrityIncident(c, 'posted-sale-terminal-disconnected');
return { ...result, appendedEvents: [], reviewRequired: reviews.length > 0 };
}
catch (error) {
if (error instanceof CanonicalInboxError) throw new CustodyIntegrityError(`canonical-inbox-${error.code}`);
throw error;
}
});
if (result.reviewRequired) {
this.integrityHold = true;
this.auditState = { ...this.auditState, status: 'failed', lastAttemptAt: Date.now(), failureCode: 'posted-sale-terminal-disconnected' };
}
return result;
}
/** Consume one bounded page directly from this process's locally validated ledger; no HTTP/peer feed. */
async observeLedgerCanonicalEvents(ledger: Ledger) {