1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204 | // EXCH/1's matching fold, against the committed test-dist build.
//
// Two kinds of claim, exactly as banca's fold tests split them:
//
// · the MATCHING RULES — what opens, what fills, what a cancel closes, what
// is inert — checked against hand-computed expectations;
// · ORDER INDEPENDENCE, which needs no expectation: the same entry set in
// several insertion orders must produce a byte-identical snapshot. That is
// the property that keeps two replicas from ever partitioning a book, and
// in a book it is also what makes priority un-frontrunnable.
import test from "node:test";
import assert from "node:assert/strict";
import * as L from "../test-dist/testlib.js";
import {
ACTORS, PAIR, SEED_MAKER, SEED_TAKER, TS_FIXED,
entry, makeCancel, makeQuote, opBank, opCancel, opLeg, opQuote, opTake,
permutations,
} from "./vectors/independent.mjs";
const { makerPub: MAKER, takerPub: TAKER, bankBPub } = ACTORS;
const T = TS_FIXED;
const PX = { num: 3, den: 2 };
/** A fresh fold over `entries`, all signature verdicts landed. */
async function fold(entries) {
const f = new L.BursaFold();
for (const e of structuredClone(entries)) f.ingest(e);
await f.awaitVerified();
return f;
}
const snap = async (entries) => (await fold(entries)).snapshot();
const quoteOf = (s, h) => s.quotes.find((q) => q.h === h);
const matchOf = (s, h) => s.matches.find((m) => m.m === h);
const ASK = makeQuote({ seed: SEED_MAKER, idN: 1, side: "ask", px: PX, qty: 1000 });
// ---- the book ----------------------------------------------------------------
test("a quote opens, a take fills, and the arithmetic is the maker's rounding", async () => {
const s = await snap([
entry("h01", 1, MAKER, opQuote(T, ASK)),
entry("h02", 2, TAKER, opTake(T, "h01", 300)),
]);
const q = quoteOf(s, "h01");
assert.equal(q.status, "open");
assert.equal(q.remainingExact, "700");
const m = matchOf(s, "h02");
assert.equal(m.maker, MAKER);
assert.equal(m.taker, TAKER);
assert.equal(m.fill, 300);
assert.equal(m.quoteAmt, 450, "300 × 3/2, exact");
assert.equal(m.qh, "h01");
});
test("a partial fill is immediate-or-cancel against available depth", async () => {
const s = await snap([
entry("h01", 1, MAKER, opQuote(T, ASK)),
entry("h02", 2, TAKER, opTake(T, "h01", 5000)),
]);
assert.equal(matchOf(s, "h02").fill, 1000, "the taker gets what was left");
assert.equal(quoteOf(s, "h01").status, "spent");
assert.equal(quoteOf(s, "h01").remainingExact, "0");
});
test("two takers racing one quote resolve by (clock, hash) — and everyone agrees", async () => {
const race = [
entry("h01", 1, MAKER, opQuote(T, ASK)),
entry("h02", 2, TAKER, opTake(T, "h01", 800)),
entry("h03", 2, ACTORS.bankBPub, opTake(T, "h01", 800)), // same clock: hash breaks it
];
const s = await snap(race);
assert.equal(matchOf(s, "h02").fill, 800, "h02 sorts first");
assert.equal(matchOf(s, "h03").fill, 200, "h03 gets the remainder");
});
test("a cancel closes its quote FROM ITS POSITION — earlier takes stand", async () => {
const s = await snap([
entry("h01", 1, MAKER, opQuote(T, ASK)),
entry("h02", 2, TAKER, opTake(T, "h01", 100)),
entry("h03", 3, MAKER, opCancel(T, makeCancel({ seed: SEED_MAKER, h: "h01" }))),
entry("h04", 4, TAKER, opTake(T, "h01", 100)),
]);
assert.ok(matchOf(s, "h02"), "the take before the cancel filled");
assert.equal(matchOf(s, "h04"), undefined, "the one after found the quote closed");
assert.equal(quoteOf(s, "h01").status, "cancelled");
});
test("only the maker's own cancel counts", async () => {
const s = await snap([
entry("h01", 1, MAKER, opQuote(T, ASK)),
entry("h02", 2, TAKER, opCancel(T, makeCancel({ seed: SEED_TAKER, h: "h01" }))),
]);
assert.equal(quoteOf(s, "h01").status, "open", "a stranger's cancel is inert");
});
test("inert takes: unknown quote, self-take, zero-settling fill", async () => {
const BID_TINY = makeQuote({ seed: SEED_MAKER, idN: 2, side: "bid", px: { num: 1, den: 2 }, qty: 1000 });
const s = await snap([
entry("h01", 1, MAKER, opQuote(T, ASK)),
entry("h02", 2, MAKER, opTake(T, "h01", 100)), // the maker taking itself
entry("h03", 3, TAKER, opTake(T, "h99", 100)), // a quote that does not exist
entry("h04", 4, MAKER, opQuote(T, BID_TINY)),
entry("h05", 5, TAKER, opTake(T, "h04", 1)), // quote leg floors to ZERO
]);
assert.equal(s.matches.length, 0, "all three are inert — no record, nothing consumed");
assert.equal(quoteOf(s, "h01").remainingExact, "1000");
assert.equal(quoteOf(s, "h04").remainingExact, "1000");
});
test("one signed quote books once, however many entries carry it", async () => {
// A mailbox relay and a live append are the same quote; the dedup key is the
// signature itself, which canonical base64url makes unique per artifact.
const s = await snap([
entry("h01", 1, MAKER, opQuote(T, ASK)),
entry("h02", 2, TAKER, opQuote(T + 1, ASK)), // re-carried by someone else
]);
assert.equal(s.quotes.length, 1);
});
test("the directory: first publication of a token wins, and a v1 link folds", async () => {
const S = "A".repeat(43);
const v2 = `${S}.${bankBPub}.2`;
const s = await snap([
entry("h01", 1, MAKER, opBank(T, v2)),
entry("h02", 2, TAKER, opBank(T, v2)), // repeat: inert
entry("h03", 3, TAKER, opBank(T, `${S}.${bankBPub}`)), // BANK/1: kept, marked
]);
assert.equal(s.banks.length, 2);
assert.equal(s.banks[0].from, MAKER, "first publisher");
assert.equal(s.banks[0].v, 2);
assert.equal(s.banks[1].v, 1, "the view says WHY this one is untradable");
});
test("a leg attaches only from a party to the match, once per lock", async () => {
const s = await snap([
entry("h01", 1, MAKER, opQuote(T, ASK)),
entry("h02", 2, TAKER, opTake(T, "h01", 100)),
entry("h03", 3, MAKER, opLeg(T, "h02", "zdpuMakerLock")),
entry("h04", 4, MAKER, opLeg(T, "h02", "zdpuMakerLock")), // dup: inert
entry("h05", 5, bankBPub, opLeg(T, "h02", "zdpuSpam")), // a stranger: inert
]);
assert.equal(matchOf(s, "h02").legs.length, 1);
assert.equal(matchOf(s, "h02").legs[0].lh, "zdpuMakerLock");
});
// ---- the envelope gate ----------------------------------------------------------
test("a crafted clock is dropped at ingest — priority cannot be bent", async () => {
const good = entry("h01", 1, MAKER, opQuote(T, ASK));
const bad = [
entry("h02", "abc", TAKER, opTake(T, "h01", 100)),
entry("h03", NaN, TAKER, opTake(T, "h01", 100)),
entry("h04", -1, TAKER, opTake(T, "h01", 100)),
entry("h05", 2 ** 53, TAKER, opTake(T, "h01", 100)),
entry("", 6, TAKER, opTake(T, "h01", 100)),
];
const s = await snap([good, ...bad]);
assert.equal(s.matches.length, 0, "every crafted envelope was refused");
assert.equal(quoteOf(s, "h01").remainingExact, "1000");
});
// ---- order independence -----------------------------------------------------------
const SCENARIOS = {
"fill and partial": [
entry("h01", 1, MAKER, opQuote(T, ASK)),
entry("h02", 2, TAKER, opTake(T, "h01", 300)),
entry("h03", 3, bankBPub, opTake(T, "h01", 900)),
],
"cancel between takes": [
entry("h01", 1, MAKER, opQuote(T, ASK)),
entry("h02", 2, TAKER, opTake(T, "h01", 100)),
entry("h03", 3, MAKER, opCancel(T, makeCancel({ seed: SEED_MAKER, h: "h01" }))),
entry("h04", 4, TAKER, opTake(T, "h01", 100)),
],
"directory and legs": [
entry("h01", 1, MAKER, opBank(T, `${"A".repeat(43)}.${bankBPub}.2`)),
entry("h02", 2, MAKER, opQuote(T, ASK)),
entry("h03", 3, TAKER, opTake(T, "h02", 500)),
entry("h04", 4, TAKER, opLeg(T, "h03", "zdpuTakerLock")),
],
};
for (const [name, entries] of Object.entries(SCENARIOS)) {
test(`order independence — ${name}`, async () => {
const want = JSON.stringify(await snap(entries));
for (const [i, o] of permutations(entries).entries()) {
assert.equal(JSON.stringify(await snap(o)), want, `insertion order ${i}`);
}
});
}
test("a fresh fold snapshots BEFORE any entry — the empty market's first paint", async () => {
// Found live: every other test ingests before it snapshots, so a nil initial
// state passed 67 tests and crashed on the floor's first render.
const f = new L.BursaFold();
const s = f.snapshot();
assert.deepEqual(s.banks, []);
assert.deepEqual(s.quotes, []);
assert.deepEqual(s.matches, []);
assert.equal(s.ok, true);
});
|