Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 57 additions & 9 deletions scripts/wasmer_size_budget.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
OUT = ROOT / "test-results" / "j-wasmer-packaging" / "size-budget.json"

# Soft ceiling for browser cdylib (~101 KiB today). Fail if regresses past this.
# ENI6MA Path-B wasm is intentionally larger and is digest-checked only (not size-budgeted).
BROWSER_BUDGET_BYTES = 150_000
BROWSER_NAME = "compass_core_bg.wasm"

Expand All @@ -26,17 +27,62 @@ def sha256(path: Path) -> str:


def parse_sums(text: str) -> dict[str, str]:
"""Map logical artifact key -> digest.

Keys are basenames for flat compass artifacts, or repo-relative paths under
wasmer/artifacts/ when the SUMS line includes a nested path (e.g. eni6ma/...).
"""
out: dict[str, str] = {}
for line in text.splitlines():
line = line.strip()
if not line:
continue
digest, name = line.split(None, 1)
name = Path(name).name
out[name] = digest
key = _sum_key(name)
out[key] = digest
return out


def _sum_key(name_field: str) -> str:
raw = Path(name_field)
parts = list(raw.parts)
# Normalize legacy "../artifacts/..." entries written relative to wasmer/.
while parts and parts[0] == "..":
parts = parts[1:]
if parts and parts[0] == "artifacts":
parts = parts[1:]
if not parts:
return raw.name
# Flat file in ART root → basename key (backward compatible).
if len(parts) == 1:
return parts[0]
return "/".join(parts)


def resolve_artifact(key: str) -> Path | None:
"""Resolve a SUMS key to a file under wasmer/artifacts/."""
direct = ART / key
if direct.is_file():
return direct
# Basename fallback + unique rglob (nested Path-B layouts).
base = Path(key).name
flat = ART / base
if flat.is_file():
return flat
matches = [p for p in ART.rglob(base) if p.is_file()]
if len(matches) == 1:
return matches[0]
if len(matches) > 1:
# Prefer path that ends with the key when key is nested.
for m in matches:
try:
if m.relative_to(ART).as_posix() == key:
return m
except ValueError:
pass
return None


def main() -> int:
OUT.parent.mkdir(parents=True, exist_ok=True)
errors: list[str] = []
Expand All @@ -50,16 +96,18 @@ def main() -> int:
expected = parse_sums(SUMS.read_text(encoding="utf-8"))
sizes: dict[str, int] = {}
actual: dict[str, str] = {}
for name, digest in sorted(expected.items()):
path = ART / name
if not path.is_file():
errors.append(f"missing artifact: {name}")
for key, digest in sorted(expected.items()):
path = resolve_artifact(key)
if path is None:
errors.append(f"missing artifact: {key}")
continue
got = sha256(path)
actual[name] = got
sizes[name] = path.stat().st_size
# Report under basename for flat keys; nested keys keep relative path.
report_name = Path(key).name if "/" not in key else key
actual[report_name] = got
sizes[report_name] = path.stat().st_size
if got != digest:
errors.append(f"SHA256 mismatch {name}: expected {digest} got {got}")
errors.append(f"SHA256 mismatch {key}: expected {digest} got {got}")

browser_size = sizes.get(BROWSER_NAME)
if browser_size is None:
Expand Down
15 changes: 15 additions & 0 deletions wasmer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,21 @@ wasmer run wasmer/artifacts/compass-decide.wasm --volume "$PWD/wasmer:/wasmer" -
Native Python `compass.core.decide_from_snapshot` is the reference for reason codes.
Divergence is a release blocker.

## ENI6MA Path-B stub (agent)

Digest-pinned circuit load + `build_minimal_proof` smoke:

- `artifacts/pins.json` — authority digests (compass + `eni6ma_demo_wasm_v1`)
- `artifacts/eni6ma/demo-wasm/v1/` — published WASM + sibling `.sha256` + wasm-bindgen `pkg/`
- `browser/circuitLoader.js`, `wasmerRunner.js`, `agent.html`

```bash
cd wasmer && python3 -m http.server 8765
# http://127.0.0.1:8765/browser/agent.html
```

Fail closed on digest mismatch. Full Gate envelope is not in this stub yet.

## Browser sandbox

```bash
Expand Down
7 changes: 4 additions & 3 deletions wasmer/artifacts/SHA256SUMS
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
e77301bed6f3bcdf8541ba7256cb6a4e58e1da62d7a98edb52fa27bdc1fee553 ../artifacts/compass-decide.wasm
9ad58acccd85e361baf9a789cdd82e95cb264dd9ddc9691236200c6ceb2507db ../artifacts/compass_core.wasm
9ad58acccd85e361baf9a789cdd82e95cb264dd9ddc9691236200c6ceb2507db ../artifacts/compass_core_bg.wasm
e77301bed6f3bcdf8541ba7256cb6a4e58e1da62d7a98edb52fa27bdc1fee553 compass-decide.wasm
9ad58acccd85e361baf9a789cdd82e95cb264dd9ddc9691236200c6ceb2507db compass_core.wasm
9ad58acccd85e361baf9a789cdd82e95cb264dd9ddc9691236200c6ceb2507db compass_core_bg.wasm
853717e421a36fc93d0791d3f2718ecf3e9c449fb3c60d4084dedab3af75c389 eni6ma/demo-wasm/v1/eni6ma_wasm.wasm
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
853717e421a36fc93d0791d3f2718ecf3e9c449fb3c60d4084dedab3af75c389
262 changes: 262 additions & 0 deletions wasmer/artifacts/eni6ma/demo-wasm/v1/pkg/eni6ma_wasm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
/**
* Build minimal proof from challenge JSON and bearings JSON.
*
* Challenge format: either `{ "challenge_nonce": MinimalNonce }` or bare `MinimalNonce`.
* Bearings format: `["U","L","R",...]` (bearing letters per round).
*
* Returns `Ok(MinimalProofPayload)` with tau, bearings, and a SHA-256 proof_hash over
* `(tau || bearings)`. This is a commitment helper for the extension until full ALGO1
* witness synthesis is available on wasm32.
* @param {string} challenge_json
* @param {string} bearings_json
* @returns {any}
*/
export function build_minimal_proof(challenge_json, bearings_json) {
const ptr0 = passStringToWasm0(challenge_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(bearings_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
const ret = wasm.build_minimal_proof(ptr0, len0, ptr1, len1);
if (ret[2]) {
throw takeFromExternrefTable0(ret[1]);
}
return takeFromExternrefTable0(ret[0]);
}
function __wbg_get_imports() {
const import0 = {
__proto__: null,
__wbg_String_8564e559799eccda: function(arg0, arg1) {
const ret = String(arg1);
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
},
__wbg___wbindgen_throw_344f42d3211c4765: function(arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
},
__wbg_new_32b398fb48b6d94a: function() {
const ret = new Array();
return ret;
},
__wbg_new_da52cf8fe3429cb2: function() {
const ret = new Object();
return ret;
},
__wbg_set_6be42768c690e380: function(arg0, arg1, arg2) {
arg0[arg1] = arg2;
},
__wbg_set_8a16b38e4805b298: function(arg0, arg1, arg2) {
arg0[arg1 >>> 0] = arg2;
},
__wbindgen_cast_0000000000000001: function(arg0, arg1) {
// Cast intrinsic for `Ref(String) -> Externref`.
const ret = getStringFromWasm0(arg0, arg1);
return ret;
},
__wbindgen_cast_0000000000000002: function(arg0, arg1) {
// Cast intrinsic for `U128 -> Externref`.
const ret = (BigInt.asUintN(64, arg0) | (BigInt.asUintN(64, arg1) << BigInt(64)));
return ret;
},
__wbindgen_init_externref_table: function() {
const table = wasm.__wbindgen_externrefs;
const offset = table.grow(4);
table.set(0, undefined);
table.set(offset + 0, undefined);
table.set(offset + 1, null);
table.set(offset + 2, true);
table.set(offset + 3, false);
},
};
return {
__proto__: null,
"./eni6ma_wasm_bg.js": import0,
};
}

let cachedDataViewMemory0 = null;
function getDataViewMemory0() {
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
}
return cachedDataViewMemory0;
}

function getStringFromWasm0(ptr, len) {
return decodeText(ptr >>> 0, len);
}

let cachedUint8ArrayMemory0 = null;
function getUint8ArrayMemory0() {
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
}
return cachedUint8ArrayMemory0;
}

function passStringToWasm0(arg, malloc, realloc) {
if (realloc === undefined) {
const buf = cachedTextEncoder.encode(arg);
const ptr = malloc(buf.length, 1) >>> 0;
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
WASM_VECTOR_LEN = buf.length;
return ptr;
}

let len = arg.length;
let ptr = malloc(len, 1) >>> 0;

const mem = getUint8ArrayMemory0();

let offset = 0;

for (; offset < len; offset++) {
const code = arg.charCodeAt(offset);
if (code > 0x7F) break;
mem[ptr + offset] = code;
}
if (offset !== len) {
if (offset !== 0) {
arg = arg.slice(offset);
}
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
const ret = cachedTextEncoder.encodeInto(arg, view);

offset += ret.written;
ptr = realloc(ptr, len, offset, 1) >>> 0;
}

WASM_VECTOR_LEN = offset;
return ptr;
}

function takeFromExternrefTable0(idx) {
const value = wasm.__wbindgen_externrefs.get(idx);
wasm.__externref_table_dealloc(idx);
return value;
}

let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
cachedTextDecoder.decode();
const MAX_SAFARI_DECODE_BYTES = 2146435072;
let numBytesDecoded = 0;
function decodeText(ptr, len) {
numBytesDecoded += len;
if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {
cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
cachedTextDecoder.decode();
numBytesDecoded = len;
}
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
}

const cachedTextEncoder = new TextEncoder();

if (!('encodeInto' in cachedTextEncoder)) {
cachedTextEncoder.encodeInto = function (arg, view) {
const buf = cachedTextEncoder.encode(arg);
view.set(buf);
return {
read: arg.length,
written: buf.length
};
};
}

let WASM_VECTOR_LEN = 0;

let wasmModule, wasmInstance, wasm;
function __wbg_finalize_init(instance, module) {
wasmInstance = instance;
wasm = instance.exports;
wasmModule = module;
cachedDataViewMemory0 = null;
cachedUint8ArrayMemory0 = null;
wasm.__wbindgen_start();
return wasm;
}

async function __wbg_load(module, imports) {
if (typeof Response === 'function' && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
const validResponse = module.ok && expectedResponseType(module.type);

if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') {
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);

} else { throw e; }
}
}

const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
const instance = await WebAssembly.instantiate(module, imports);

if (instance instanceof WebAssembly.Instance) {
return { instance, module };
} else {
return instance;
}
}

function expectedResponseType(type) {
switch (type) {
case 'basic': case 'cors': case 'default': return true;
}
return false;
}
}

function initSync(module) {
if (wasm !== undefined) return wasm;


if (module !== undefined) {
if (Object.getPrototypeOf(module) === Object.prototype) {
({module} = module)
} else {
console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
}
}

const imports = __wbg_get_imports();
if (!(module instanceof WebAssembly.Module)) {
module = new WebAssembly.Module(module);
}
const instance = new WebAssembly.Instance(module, imports);
return __wbg_finalize_init(instance, module);
}

async function __wbg_init(module_or_path) {
if (wasm !== undefined) return wasm;


if (module_or_path !== undefined) {
if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
({module_or_path} = module_or_path)
} else {
console.warn('using deprecated parameters for the initialization function; pass a single object instead')
}
}

if (module_or_path === undefined) {
module_or_path = new URL('eni6ma_wasm_bg.wasm', import.meta.url);
}
const imports = __wbg_get_imports();

if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
module_or_path = fetch(module_or_path);
}

const { instance, module } = await __wbg_load(await module_or_path, imports);

return __wbg_finalize_init(instance, module);
}

export { initSync, __wbg_init as default };
Binary file not shown.
Loading
Loading