bursa / client / test / artifacts.test.mjs
  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
// The two bursa artifacts, against the committed test-dist build and the
// independent derivation path. Every signature here came from @noble/curves
// against hand-written preimage templates, so nothing under test produced its
// own inputs.

import test from "node:test";
import assert from "node:assert/strict";
import * as L from "../test-dist/testlib.js";
import {
  ACTORS, CUR_BASE, CUR_QUOTE, PAIR, SEED_MAKER, SEED_TAKER, TS_FIXED,
  makeCancel, makeQuote, quotePreimageByHand,
} from "./vectors/independent.mjs";

const PX = { num: 3, den: 2 };
const Q = makeQuote({ seed: SEED_MAKER, idN: 1, side: "ask", px: PX, qty: 1000, ctx: "" });
const C = makeCancel({ seed: SEED_MAKER, h: "h05" });

// ---- the wire ----------------------------------------------------------------

test("a quote verifies against an independently-produced signature", async () => {
  assert.ok(await L.verifyQuote(Q));
  assert.ok(await L.verifyCancel(C));
});

test("the kit's preimage is byte-identical to the hand-written template", () => {
  assert.equal(L.quotePreimage(Q), quotePreimageByHand(Q));
  assert.ok(L.quotePreimage(Q).startsWith(`${L.DOM_ORD}|v1|`));
  assert.ok(L.cancelPreimage(C).startsWith(`${L.DOM_CXL}|v1|`));
});

test("the transmitted key order is the documented one", () => {
  assert.deepEqual(Object.keys(Q),
    ["v", "t", "id", "pair", "side", "px", "qty", "from", "ctx", "ts", "exp", "sig"]);
  assert.deepEqual(Object.keys(Q.px), ["num", "den"]);
  assert.deepEqual(Object.keys(C), ["v", "t", "h", "from", "ts", "sig"]);
});

test("key order is the wire but not the signature — a reversed form verifies", async () => {
  const rev = Object.fromEntries(Object.entries(Q).reverse());
  rev.px = { den: PX.den, num: PX.num };
  assert.notEqual(JSON.stringify(rev), JSON.stringify(Q));
  assert.ok(await L.verifyQuote(rev), "canon() sorts, so the same bytes were signed");
});

test("tampered artifacts verify to null", async () => {
  const rows = [
    ["sig flipped", { ...Q, sig: (Q.sig[0] === "A" ? "B" : "A") + Q.sig.slice(1) }],
    ["qty raised", { ...Q, qty: 100000 }],
    ["price moved", { ...Q, px: { num: 4, den: 2 } }],
    ["side flipped", { ...Q, side: "bid" }],
    ["pair swapped", { ...Q, pair: `${CUR_QUOTE}|${CUR_BASE}` }],
    ["extra key stapled on", { ...Q, evil: 1 }],
    ["sig removed", (() => { const x = { ...Q }; delete x.sig; return x; })()],
    ["relabelled as a cancel", { ...Q, t: "bxc" }],
  ];
  for (const [name, artifact] of rows) {
    assert.equal(await L.verifyQuote(artifact), null, name);
  }
  assert.equal(await L.verifyCancel({ ...C, h: "other" }), null, "cancel: h rewritten");
  assert.equal(await L.verifyCancel({ ...C, from: ACTORS.takerPub }), null, "cancel: maker swapped");
});

test("neither artifact is readable as the other, or as any wallet-kit one", () => {
  assert.equal(L.quoteShape(C), null);
  assert.equal(L.cancelShape(Q), null);
});

// ---- the pair ------------------------------------------------------------------

test("a pair is two full currency ids, distinct, and never the neutral unit", () => {
  assert.deepEqual(L.parsePair(PAIR), { base: CUR_BASE, quote: CUR_QUOTE });
  const bad = [
    `${CUR_BASE}|${CUR_BASE}`,           // one currency with itself
    `${CUR_BASE}|~.GAZ`,                 // quote-only unit: nothing to lock
    `~.GAZ|${CUR_BASE}`,
    `DUH|MOR`,                           // codes without issuers are not currencies
    CUR_BASE, "", "|", `${CUR_BASE}|`, null, 7, {},
  ];
  for (const p of bad) assert.equal(L.parsePair(p), null, JSON.stringify(p));
});

// ---- prices ----------------------------------------------------------------------

test("price comparison is BigInt cross-multiplication, never a double", () => {
  // 2^50-scale numerators overflow a double product; cross-multiplication in
  // BigInt does not. These two prices differ by one part in 2^50.
  const big = 2 ** 50;
  assert.equal(L.pxCmp({ num: big, den: big - 1 }, { num: big - 1, den: big - 2 }), -1);
  assert.equal(L.pxCmp({ num: 3, den: 2 }, { num: 6, den: 4 }), 0, "equal as rationals");
  assert.equal(L.pxCmp({ num: 2, den: 1 }, { num: 3, den: 2 }), 1);
});

test("the quote-leg rounding always favours the maker", () => {
  // ask (taker pays): rounds UP — grinding a fill into pieces overpays per piece
  assert.equal(L.quoteLegAmt("ask", { num: 1, den: 2 }, 1), 1, "ceil(0.5)");
  assert.equal(L.quoteLegAmt("ask", { num: 1, den: 2 }, 100), 50, "exact stays exact");
  // bid (maker pays): rounds DOWN — the maker cannot be ground out one piece at a time
  assert.equal(L.quoteLegAmt("bid", { num: 1, den: 2 }, 100), 50);
  assert.equal(L.quoteLegAmt("bid", { num: 3, den: 2 }, 3), 4, "floor(4.5)");
});

test("a fill whose quote leg cannot settle answers nil, and that is a verdict", () => {
  // floor to ZERO: no second lock, the atomic construction degenerates
  assert.equal(L.quoteLegAmt("bid", { num: 1, den: 2 }, 1), null);
  // past 2^50: no lock can carry it
  assert.equal(L.quoteLegAmt("ask", { num: 2 ** 50, den: 1 }, 2), null);
  // …and the largest exact case still works
  assert.equal(L.quoteLegAmt("ask", { num: 2 ** 50, den: 1 }, 1), 2 ** 50);
});

// ---- the ops -----------------------------------------------------------------------

test("every op builder emits its documented key order", () => {
  assert.deepEqual(Object.keys(L.mkBank("s")), ["t", "ts", "s"]);
  assert.deepEqual(Object.keys(L.mkQuote(Q)), ["t", "ts", "o"]);
  assert.deepEqual(Object.keys(L.mkCancel(C)), ["t", "ts", "c"]);
  assert.deepEqual(Object.keys(L.mkTake("h05", 10)), ["t", "ts", "h", "qty"]);
  assert.deepEqual(Object.keys(L.mkLeg("h07", "zdpuLock")), ["t", "ts", "m", "lh"]);
});

test("the validators are the contract — exact key sets, no extension slot", () => {
  assert.equal(L.validQuote(L.mkQuote(Q)), true);
  assert.equal(L.validQuote({ ...L.mkQuote(Q), evil: 1 }), false);
  assert.equal(L.validTake(L.mkTake("h05", 10)), true);
  assert.equal(L.validTake({ t: "take", ts: 0, h: "h05", qty: 0 }), false, "zero is not a quantity");
  assert.equal(L.validOp({ t: "future-op", ts: 0 }), false, "an op the fold cannot judge");
  assert.equal(L.validOp(null), false);
});

test("a bank op carries a whole link — a bare secret is refused, a v1 link folds", () => {
  const S = "A".repeat(43);
  const B = ACTORS.bankBPub;
  assert.equal(L.validBank(L.mkBank(`${S}.${B}.2`)), true, "a BANK/2 link");
  assert.equal(L.validBank(L.mkBank(`${S}.${B}`)), true,
    "a BANK/1 link FOLDS — the view marks it untradable with the reason in words");
  assert.equal(L.validBank(L.mkBank(S)), false, "a bare secret is not a bank");
  assert.equal(L.validBank(L.mkBank("nonsense")), false);
});

test("a pasted bank URL, fragment or bare token all yield the token", () => {
  // Found live: the first real user pasted their bank's URL and was told
  // "that is not a bank link" — both were right, which means the input was
  // wrong. Anchored like banca's own join field: no bank hides inside a JWT.
  const S = "A".repeat(43), B = ACTORS.bankBPub;
  for (const tok of [`${S}.${B}`, `${S}.${B}.2`]) {
    assert.equal(L.bankTokenOf(tok), tok, "bare");
    assert.equal(L.bankTokenOf(`https://banca.ardegazu.ro/#${tok}`), tok, "URL");
    assert.equal(L.bankTokenOf(`#${tok}&relay`), tok, "fragment with params");
    assert.equal(L.bankTokenOf(`  ${tok}  `), tok, "whitespace");
  }
  assert.equal(L.bankTokenOf(S), null, "a bare secret is not a bank");
  assert.equal(L.bankTokenOf(`xx${S}.${B}`), null, "anchored — no bank inside a blob");
  assert.equal(L.bankTokenOf(null), null);
});

static mirror of HEAD · about · clone: git clone https://git.ardegazu.ro/bursa.git