@xmbl/simulator 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -2
- package/DEVNET-SEAM-FINDING.md +2 -2
- package/package.json +1 -1
- package/src/capabilities.js +5 -5
- package/src/devnet-rpc.js +2 -2
- package/src/devnet-rpc.test.mjs +1 -1
- package/src/simulator.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.13
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- **SECURITY — the AIR zero-knowledge path leaked the entire secret trace.** Reported against
|
|
8
|
+
0.1.12 and reproduced: the batching challenges `alpha` and `beta` are public and the composition
|
|
9
|
+
`C(x) = alpha*u + beta*v` is an `F_p^4` element over two base-field unknowns, so every FRI opening
|
|
10
|
+
was four equations in two unknowns. Solving two limbs for `v` gives `col'(x)` directly, and the
|
|
11
|
+
`2*nq` layer-0 openings interpolate the blinded trace column — row 0 IS the witness. It returned
|
|
12
|
+
the secret exactly, in about a second. The `(x^T - 1)` blind could not help: it is sized for the
|
|
13
|
+
`2*nc` direct openings and vanishes on the trace domain by construction.
|
|
14
|
+
|
|
15
|
+
The composition is now MASKED before FRI sees it: a uniformly random extension-valued polynomial
|
|
16
|
+
`M` of degree `< K` is committed first, `gamma` is drawn from a transcript that includes its root,
|
|
17
|
+
and FRI runs on `C + gamma*M`. Each opening is four equations in six unknowns. The verifier opens
|
|
18
|
+
`M` against its own root at the consistency points. A mask that is not of degree `< K` would pin
|
|
19
|
+
`gamma` to one value out of ~2^124, so masking cannot hide a false statement.
|
|
20
|
+
|
|
21
|
+
`setup` now doubles `K` until it exceeds everything the transcript reveals
|
|
22
|
+
(`nq*(log2 K + 1) + nc`, plus margin): `K = 2048`, `N = 16384`. `nc` is raised 16 -> 40, because
|
|
23
|
+
each consistency point is `-log2(K/N) = 3` bits and `nc = 16` was a ~48-bit check behind a
|
|
24
|
+
~100-bit FRI. Coset evaluation is now an NTT, so proving is faster than 0.1.12 despite the larger
|
|
25
|
+
domain. **PROOFS FROM 0.1.12 DO NOT VERIFY ON 0.1.13** — the proof carries new commitments
|
|
26
|
+
(`rootM`, `rootD`) and the derived parameters moved.
|
|
27
|
+
|
|
28
|
+
`xzk` (the cube-curve statement) was attacked the same way and survives: the committed curve is
|
|
29
|
+
fully recoverable from its FRI openings, but `Pt = P + Z_R*B` with `B` uniform of degree 18 over a
|
|
30
|
+
witness quotient of degree 1, so the same proof is carried by a 2-parameter family of witnesses.
|
|
31
|
+
|
|
32
|
+
The attack is now a permanent check in `air.test.mjs` and `xzk.test.mjs`.
|
|
33
|
+
|
|
34
|
+
## 0.1.12
|
|
35
|
+
|
|
3
36
|
## 0.1.11
|
|
4
37
|
|
|
5
38
|
### Patch Changes
|
|
@@ -18,7 +51,7 @@
|
|
|
18
51
|
never under the xid it claims (a datum fails `validateXid` precisely when that xid is somebody else's), and
|
|
19
52
|
the anchor dedup key claimed before validation is released on every failure. Both doors are closed on BOTH
|
|
20
53
|
entry points — `addTransaction` and `addSealedBatch`, the path a finalized transaction actually takes — and
|
|
21
|
-
one invalid entry in a batch no longer discards the
|
|
54
|
+
one invalid entry in a batch no longer discards the genuine transactions behind it.
|
|
22
55
|
- consensus: a content-addressed type-6 value tx is admitted on its content address (it carries no in-body
|
|
23
56
|
signature by design); a malformed anchor is refused at the door by the ledger's own `validateTransaction`;
|
|
24
57
|
`finalizeTransaction` preserves the signed `id`.
|
|
@@ -28,7 +61,7 @@
|
|
|
28
61
|
- networking: self-elected circuit-relay server; NO_FATAL transport tolerance; throttled bootstrap warnings.
|
|
29
62
|
- storage-compute: `CoordinateDelivery` (broken: `require` in ESM, keyed on the public key) is removed.
|
|
30
63
|
- core: ships the node daemon as the `xmbl-node` bin; the control socket implements the coordinator
|
|
31
|
-
contract (`xsc`, `submit_batch`, `ledger_capabilities`, `identity_status`, a signed `chain` claim,
|
|
64
|
+
contract (`xsc`, `submit_batch`, `ledger_capabilities`, `identity_status`, a signed `chain` claim, genuine
|
|
32
65
|
`submit_tx` rejections) and reports the RUNNING versions; boot waits for the stores.
|
|
33
66
|
- every package exports a load-time `VERSION`.
|
|
34
67
|
- PROTOCOL (operator, 2026-09-16): every transaction is typed by its xid (tokens.json type codes; `micromineTx`/
|
package/DEVNET-SEAM-FINDING.md
CHANGED
|
@@ -5,7 +5,7 @@ opts into ledger-side signature verification — it wires **both** `xid` and
|
|
|
5
5
|
`getPublicKeyByAddress` into the `Ledger` — which is precisely the caller configuration that
|
|
6
6
|
exercises defects no production code path currently reaches. Two of the three defects below are
|
|
7
7
|
now **FIXED**; the third is a genuine signature-domain decision left for the audit and documented
|
|
8
|
-
|
|
8
|
+
accurately rather than papered over. All are covered by `src/devnet.test.mjs`.
|
|
9
9
|
|
|
10
10
|
## Where ledger-side verification runs
|
|
11
11
|
|
|
@@ -92,7 +92,7 @@ architectural choices, **which is an audit-level signature-domain / block-identi
|
|
|
92
92
|
Both touch `block.js`, ledger dedup, and cross-node determinism, so neither rides in on a tooling
|
|
93
93
|
commit. Until one is made and audited, **ledger-side re-verification stays OFF in production**
|
|
94
94
|
(the `getPublicKeyByAddress` lookup is deliberately not wired into the `Ledger`), and consensus
|
|
95
|
-
remains the single verification point. This is the
|
|
95
|
+
remains the single verification point. This is the genuine, current posture — not a silent gap.
|
|
96
96
|
|
|
97
97
|
## Why the devnet drives the direct path
|
|
98
98
|
|
package/package.json
CHANGED
package/src/capabilities.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// zk / homomorphic-encryption / signature primitives in-page (they need node:crypto; an MV3 page
|
|
3
3
|
// CSP and WebCrypto's async sha256 make a faithful in-page port impossible), so the devnet node
|
|
4
4
|
// process — which already loads the REAL modules — runs each primitive end-to-end and returns a
|
|
5
|
-
// JSON-safe verdict. Each runner does the
|
|
5
|
+
// JSON-safe verdict. Each runner does the right thing and its NEGATIVE control in one call: it
|
|
6
6
|
// proves the capability verifies true input AND rejects tampered input, so a green card on screen
|
|
7
7
|
// is backed by a real refusal, not a fabricated pass. Every flow mirrors the headless
|
|
8
8
|
// reproductions in @xmbl/contracts (reproductions/contract-zk.mjs, contract-he.mjs,
|
|
@@ -25,7 +25,7 @@ const CUBE_CONTEXT = {
|
|
|
25
25
|
|
|
26
26
|
// ── Coordinate / curve zero-knowledge (@xmbl/zero-knowledge, FRI, ⛔ UNAUDITED) ──
|
|
27
27
|
// A prover shows a coordinate (derivedX, derivedY) lies on a curve through secret points, and a
|
|
28
|
-
// verifier checks it WITHOUT the secret points. We prove the
|
|
28
|
+
// verifier checks it WITHOUT the secret points. We prove the genuine coordinate and reject a
|
|
29
29
|
// tampered y (derivedY+1) — the exact pair reproductions/contract-zk.mjs gates a Verkle write on.
|
|
30
30
|
export function zkProof({ derivedX = 99 } = {}) {
|
|
31
31
|
const ctx = setup();
|
|
@@ -34,14 +34,14 @@ export function zkProof({ derivedX = 99 } = {}) {
|
|
|
34
34
|
const dX = bi(derivedX);
|
|
35
35
|
const { Pt, derivedY } = blindedCurve(ctx, { publicPoints, secretPoints, derivedX: dX });
|
|
36
36
|
const proof = prove(ctx, { Pt, publicPoints, derivedX: dX, derivedY });
|
|
37
|
-
const
|
|
37
|
+
const genuine = zkVerify(ctx, { proof, publicPoints, derivedX: dX, derivedY });
|
|
38
38
|
const tampered = zkVerify(ctx, { proof, publicPoints, derivedX: dX, derivedY: derivedY + 1n });
|
|
39
39
|
return {
|
|
40
|
-
ok:
|
|
40
|
+
ok: genuine === true && tampered === false,
|
|
41
41
|
scheme: 'xzk (FRI coordinate/curve proof, UNAUDITED)',
|
|
42
42
|
derivedX: str(dX), derivedY: str(derivedY),
|
|
43
43
|
publicAnchors: publicPoints.length,
|
|
44
|
-
|
|
44
|
+
genuineVerifies: genuine, tamperedRejected: tampered === false,
|
|
45
45
|
note: 'proof checked without the secret points; a coordinate off the curve is rejected',
|
|
46
46
|
};
|
|
47
47
|
}
|
package/src/devnet-rpc.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// It ALSO serves the node-side capability surface the extension cannot run in-page — each a REAL
|
|
18
18
|
// primitive run end-to-end in this node process, verdict returned JSON-safe (see capabilities.js):
|
|
19
19
|
// getStateRoot {type} → { root, pooled, landed } // live ledger state root
|
|
20
|
-
// zkProof {type, derivedX?} → { ok, derivedX, derivedY,
|
|
20
|
+
// zkProof {type, derivedX?} → { ok, derivedX, derivedY, genuineVerifies, tamperedRejected, … }
|
|
21
21
|
// heAdd {type, a?, b?} → { ok, a, b, sum, expected, … } // homomorphic add
|
|
22
22
|
// sigVerify {type, message?} → { ok, signedVerifies, tamperedRejected, … } // Cubic-SIG
|
|
23
23
|
// seal {type, secret?} → { ok, roundTrip, … } // PQ KEM seal
|
|
@@ -69,7 +69,7 @@ export class DevnetRpc {
|
|
|
69
69
|
}
|
|
70
70
|
default: {
|
|
71
71
|
// Node-side capability surface (zk / HE / signature / seal): a real primitive run to a
|
|
72
|
-
// verdict in this process. Unknown types still fall through to
|
|
72
|
+
// verdict in this process. Unknown types still fall through to a genuine error.
|
|
73
73
|
const cap = CAPABILITIES[message && message.type];
|
|
74
74
|
if (cap) return cap(message || {});
|
|
75
75
|
return { error: `Unknown message type: ${message && message.type}` };
|
package/src/devnet-rpc.test.mjs
CHANGED
|
@@ -54,7 +54,7 @@ try {
|
|
|
54
54
|
|
|
55
55
|
// ── node-side capability surface: each a REAL primitive verified + negative-controlled ──
|
|
56
56
|
const zk = await call({ type: 'zkProof', derivedX: 99 });
|
|
57
|
-
ok('zkProof verifies the
|
|
57
|
+
ok('zkProof verifies the genuine coordinate and rejects the tampered one', zk.ok === true && zk.genuineVerifies === true && zk.tamperedRejected === true, JSON.stringify(zk));
|
|
58
58
|
|
|
59
59
|
const he1 = await call({ type: 'heAdd', a: 1, b: 0 });
|
|
60
60
|
ok('heAdd: ENC(1) ⊞ ENC(0) decrypts to 1 (blind add)', he1.ok === true && he1.sum === 1, JSON.stringify(he1));
|
package/src/simulator.js
CHANGED
|
@@ -613,7 +613,7 @@ export class SystemSimulator extends EventEmitter {
|
|
|
613
613
|
// Compute operations can trigger state changes. This used to call a fake
|
|
614
614
|
// xvsm.executeTransaction with a comment string as "WASM"; that path is gone (real
|
|
615
615
|
// contract execution is @xmbl/contracts' ContractHost over @xmbl/storage-compute). The
|
|
616
|
-
// simulator's
|
|
616
|
+
// simulator's genuine job here is just to drive a state change, so it uses the real
|
|
617
617
|
// state-diff path directly.
|
|
618
618
|
if (this.modules.xvsm && chance.bool({ likelihood: 70 })) {
|
|
619
619
|
setTimeout(() => {
|