Per-order terminal reorg linkage

This commit is contained in:
2026-10-20 19:08:19 -04:00
parent e1fbf5e5af
commit 22da9764eb
9 changed files with 98 additions and 24 deletions
+30 -4
View File
@@ -22,7 +22,7 @@ import { CUSTODY_POLICY, custodyMessage, type CustodyAction, type CustodyCommand
type CustodyListing, type CustodyOrder, type CustodyWithdrawal } from '../../shared/custody.ts';
import { NETWORK } from '../../shared/network.ts';
import { CHAIN_ID, GENESIS_HASH, address, sha256 } from '../../server/testnet/consensus.ts';
import { saleContext, termsForCustodyOrder, saleId } from '../../server/testnet/sale-candidate.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';
@@ -78,7 +78,7 @@ function canonicalPage(streamId: string, all: CanonicalInboxPage['events'], star
const anchor = events.at(-1)?.eventHash ?? previousAnchor;
return { streamId, previousAnchor, anchor, events, cursor, head: all.length, caughtUp: cursor === all.length };
}
function canonicalEvents(streamId: string) {
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[] = []) => {
@@ -88,8 +88,8 @@ function canonicalEvents(streamId: string) {
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, ['1'.repeat(64)]);
add('disconnect', 1, a, GENESIS_HASH, ['1'.repeat(64)]);
add('connect', 1, a, GENESIS_HASH, [linkedTxId]);
add('disconnect', 1, a, GENESIS_HASH, [linkedTxId]);
add('connect', 1, b, GENESIS_HASH, ['2'.repeat(64)]);
add('connect', 2, c, b, ['3'.repeat(64)]);
return rows;
@@ -631,6 +631,32 @@ test('signed candidate completion settles actual PostgreSQL balances once across
} 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);
await assert.rejects(observer.observeCanonicalEvents(canonicalPage(streamId, events, 1, 1)), /posted-sale-terminal-disconnected/);
await assert.rejects(observer.observeCanonicalEvents(canonicalPage(streamId, events, 1, 1)), /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, 1);
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(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 });