export-ton-verifier
Version:
Tool for generating Groth16 and PLONK verifier code for the TON blockchain from SnarkJS .zkey or verification key JSON files.
1,520 lines (1,498 loc) • 53.1 kB
JavaScript
// src/dictFromInputList.ts
import { Dictionary } from "@ton/core";
function dictFromInputList(list) {
const dict = Dictionary.empty(
Dictionary.Keys.Int(32),
Dictionary.Values.BigInt(256)
);
for (let i = 0; i < list.length; i++) {
dict.set(i, list[i]);
}
return dict;
}
// src/groth16CompressProof.js
import { buildBls12381 as buildBls123812, utils } from "ffjavascript";
// src/utils.js
import { buildBn128, buildBls12381 } from "ffjavascript";
async function getCurveFromName(name, options) {
let curve;
let singleThread = options && options.singleThread;
const normName = normalizeName(name);
if (["BN128", "BN254", "ALTBN128"].indexOf(normName) >= 0) {
curve = await buildBn128(singleThread);
} else if (["BLS12381"].indexOf(normName) >= 0) {
curve = await buildBls12381(singleThread);
} else {
throw new Error(`Curve not supported: ${name}`);
}
return curve;
function normalizeName(n) {
return n.toUpperCase().match(/[A-Za-z0-9]+/g).join("");
}
}
function toHexString(byteArray) {
return Array.from(
byteArray,
(byte) => ("0" + (byte & 255).toString(16)).slice(-2)
).join("");
}
function assertBls12381Curve(curve) {
const normalizedName = String((curve == null ? void 0 : curve.name) ?? "").toLowerCase().replace(/[^a-z0-9]/g, "");
if (normalizedName !== "bls12381") {
throw new Error("Compression helpers support only BLS12-381 curves.");
}
}
function g1Compressed(curve, p1Raw) {
assertBls12381Curve(curve);
const p1 = curve.G1.fromObject(p1Raw);
const buff = new Uint8Array(48);
curve.G1.toRprCompressed(buff, 0, p1);
if (buff[0] & 128) buff[0] |= 32;
buff[0] |= 128;
return toHexString(buff);
}
function g2Compressed(curve, p2Raw) {
assertBls12381Curve(curve);
const p2 = curve.G2.fromObject(p2Raw);
const buff = new Uint8Array(96);
curve.G2.toRprCompressed(buff, 0, p2);
if (buff[0] & 128) buff[0] |= 32;
buff[0] |= 128;
return toHexString(buff);
}
// src/limits.js
function normalizeTonBlsCurveName(raw, field = "curve") {
if (typeof raw !== "string" || raw.trim() === "") {
throw new Error(`${field}: BLS12-381 curve metadata is required.`);
}
const normalized = raw.toLowerCase().replace(/[^a-z0-9]/g, "");
if (normalized !== "bls12381") {
throw new Error(
`${field}: TON verifier templates use TVM BLS12-381 opcodes; got '${raw}'.`
);
}
return "bls12381";
}
function assertTonBlsCurve(raw, field = "curve") {
normalizeTonBlsCurveName(raw, field);
}
var MAX_PLONK_PUBLIC_INPUTS = 244;
function assertPlonkPublicInputCount(count, field = "public inputs") {
if (!Number.isInteger(count) || count < 0) {
throw new Error(`${field}: public input count must be a non-negative integer.`);
}
if (count > MAX_PLONK_PUBLIC_INPUTS) {
throw new Error(
`${field}: PLONK verifier supports at most ${MAX_PLONK_PUBLIC_INPUTS} public inputs because the TVM Keccak transcript is limited to 255 tuple items.`
);
}
}
// src/groth16CompressProof.js
async function groth16CompressProof(proof, publicSignals) {
assertTonBlsCurve(proof == null ? void 0 : proof.curve, "proof.curve");
const curve = await buildBls123812();
try {
const proofProc = utils.unstringifyBigInts(proof);
const pi_aS = g1Compressed(curve, proofProc.pi_a);
const pi_bS = g2Compressed(curve, proofProc.pi_b);
const pi_cS = g1Compressed(curve, proofProc.pi_c);
const pi_a = Buffer.from(pi_aS, "hex");
const pi_b = Buffer.from(pi_bS, "hex");
const pi_c = Buffer.from(pi_cS, "hex");
const pubInputs = publicSignals.map((s) => BigInt(s));
return {
pi_a,
pi_b,
pi_c,
pubInputs
};
} finally {
if (curve && typeof curve.terminate === "function") {
await curve.terminate();
}
}
}
// src/generateVerifiers.js
import fs4 from "fs/promises";
import ejs from "ejs";
import { utils as utils3 } from "ffjavascript";
// src/helpers.js
import fs from "fs/promises";
import path from "path";
import * as binFileUtils from "@iden3/binfileutils";
// src/curves.js
import { buildBls12381 as buildBls123813, Scalar } from "ffjavascript";
var bls12381q = Scalar.e(
"1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab",
16
);
async function getCurveFromQ(q) {
let curve;
if (Scalar.eq(q, bls12381q)) {
curve = await buildBls123813();
} else {
throw new Error(`Curve not supported: ${Scalar.toString(q)}`);
}
return curve;
}
async function getCurveFromName2(name, options) {
let curve;
let singleThread = options && options.singleThread;
const normName = normalizeName(name);
if (["BLS12381"].indexOf(normName) >= 0) {
curve = await buildBls123813(singleThread);
} else {
throw new Error(`Curve not supported: ${name}`);
}
return curve;
function normalizeName(n) {
return n.toUpperCase().match(/[A-Za-z0-9]+/g).join("");
}
}
// src/helpers.js
async function fileExists(p) {
try {
await fs.access(p);
return true;
} catch {
return false;
}
}
async function resolveTemplatePath(templatesDir, lang, protocol) {
const withProtocol = `${lang}_verifier_${protocol}.ejs`;
const fallback = `${lang}_verifier.ejs`;
const pathWithProtocol = path.join(templatesDir, withProtocol);
const pathFallback = path.join(templatesDir, fallback);
if (await fileExists(pathWithProtocol)) {
return pathWithProtocol;
}
if (protocol === "groth16" && await fileExists(pathFallback)) {
return pathFallback;
}
throw new Error(
`Template not found: '${withProtocol}' (or '${fallback}' for groth16) in ${templatesDir}`
);
}
function normalizeContractName(name = "MyVerifier") {
const raw = String(name ?? "").trim();
if (!raw) return "MyVerifier";
const parts = raw.match(/[A-Za-z0-9]+/g) ?? [];
if (parts.length === 0) return "MyVerifier";
let normalized = parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
if (!/^[A-Za-z_]/.test(normalized)) {
normalized = `_${normalized}`;
}
return normalized;
}
function log2(V) {
return ((V & 4294901760) !== 0 ? (V &= 4294901760, 16) : 0) | ((V & 4278255360) !== 0 ? (V &= 4278255360, 8) : 0) | ((V & 4042322160) !== 0 ? (V &= 4042322160, 4) : 0) | ((V & 3435973836) !== 0 ? (V &= 3435973836, 2) : 0) | (V & 2863311530) !== 0;
}
async function readG1(fd, curve, toObject) {
const buff = await fd.read(curve.G1.F.n8 * 2);
const res = curve.G1.fromRprLEM(buff, 0);
return toObject ? curve.G1.toObject(res) : res;
}
async function readG2(fd, curve, toObject) {
const buff = await fd.read(curve.G2.F.n8 * 2);
const res = curve.G2.fromRprLEM(buff, 0);
return toObject ? curve.G2.toObject(res) : res;
}
async function readHeaderPlonk(fd, sections, toObject) {
const zkey = {};
zkey.protocol = "plonk";
await binFileUtils.startReadUniqueSection(fd, sections, 2);
const n8q = await fd.readULE32();
zkey.n8q = n8q;
zkey.q = await binFileUtils.readBigInt(fd, n8q);
const n8r = await fd.readULE32();
zkey.n8r = n8r;
zkey.r = await binFileUtils.readBigInt(fd, n8r);
zkey.curve = await getCurveFromQ(zkey.q);
zkey.nVars = await fd.readULE32();
zkey.nPublic = await fd.readULE32();
zkey.domainSize = await fd.readULE32();
zkey.power = log2(zkey.domainSize);
zkey.nAdditions = await fd.readULE32();
zkey.nConstraints = await fd.readULE32();
zkey.k1 = await fd.read(n8r);
zkey.k2 = await fd.read(n8r);
zkey.Qm = await readG1(fd, zkey.curve, toObject);
zkey.Ql = await readG1(fd, zkey.curve, toObject);
zkey.Qr = await readG1(fd, zkey.curve, toObject);
zkey.Qo = await readG1(fd, zkey.curve, toObject);
zkey.Qc = await readG1(fd, zkey.curve, toObject);
zkey.S1 = await readG1(fd, zkey.curve, toObject);
zkey.S2 = await readG1(fd, zkey.curve, toObject);
zkey.S3 = await readG1(fd, zkey.curve, toObject);
zkey.X_2 = await readG2(fd, zkey.curve, toObject);
await binFileUtils.endReadSection(fd);
return zkey;
}
// src/templateResolver.js
import path2 from "path";
import { createRequire } from "module";
var PACKAGE_NAME = "export-ton-verifier";
function requireBases() {
const bases = [];
if (typeof __filename === "string") {
bases.push(__filename);
}
if (process.argv[1]) {
bases.push(path2.resolve(process.argv[1]));
}
bases.push(path2.join(process.cwd(), "__export_ton_verifier_resolver__.js"));
return [...new Set(bases)];
}
function packageRootFromResolvedEntry(entryPath) {
const dir = path2.dirname(entryPath);
return path2.basename(dir) === "dist" ? path2.dirname(dir) : dir;
}
function resolvePackageRootFromSelfReference() {
for (const base of requireBases()) {
try {
return packageRootFromResolvedEntry(
createRequire(base).resolve(PACKAGE_NAME)
);
} catch {
}
}
return null;
}
function resolvePackageRootFromModuleDir() {
if (typeof __dirname !== "string") return null;
if (path2.basename(__dirname) === "dist" || path2.basename(__dirname) === "src") {
return path2.dirname(__dirname);
}
return __dirname;
}
function defaultTemplatesDir() {
const packageRoot = resolvePackageRootFromSelfReference() ?? resolvePackageRootFromModuleDir() ?? process.cwd();
return path2.join(packageRoot, "templates");
}
// src/logger.js
function createLogger({ logger = null, quiet = false } = {}) {
if (quiet || logger === false || logger == null) {
return { log() {
} };
}
if (typeof logger === "function") {
return { log: logger };
}
if (typeof logger.log === "function") {
return { log: logger.log.bind(logger) };
}
throw new Error(
"logger must be a function, an object with log(message), false, null, or undefined."
);
}
// src/formats/snarkjs.js
import { zKey } from "snarkjs";
// src/export_plonk_vk.js
import * as binFileUtils2 from "@iden3/binfileutils";
import { utils as utils2 } from "ffjavascript";
var { stringifyBigInts, unstringifyBigInts } = utils2;
var G1_KEYS = ["Qm", "Ql", "Qr", "Qo", "Qc", "S1", "S2", "S3"];
var G1_UC_KEYS = ["Qm_uc", "Ql_uc", "Qr_uc", "Qo_uc", "Qc_uc", "S1_uc", "S2_uc", "S3_uc"];
async function zkeyExportPlonkVerificationKey(zkeyName) {
const { fd, sections } = await binFileUtils2.readBinFile(zkeyName, "zkey", 2);
try {
const zkey = await readHeaderPlonk(fd, sections);
return await plonkVk(zkey);
} finally {
await fd.close();
}
}
async function normalizePlonkVerificationKey(vkRaw) {
if (vkRaw.protocol !== "plonk") {
throw new Error(`Expected PLONK verification key (got '${vkRaw.protocol}').`);
}
const nPublic = numberField(vkRaw, "nPublic");
assertPlonkPublicInputCount(nPublic, "verification_key.nPublic");
if (isTemplateReadyPlonkVerificationKey(vkRaw)) {
return stringifyBigInts({ ...vkRaw, nPublic });
}
const vk = unstringifyBigInts(vkRaw);
const curve = await getCurveFromName(vkRaw.curve);
try {
const res = {
protocol: "plonk",
curve: curve.name,
nPublic,
power: numberField(vkRaw, "power"),
k1: scalarToString(curve, vk.k1, "k1"),
k2: scalarToString(curve, vk.k2, "k2"),
w: vk.w === void 0 ? scalarToString(curve, curve.Fr.w[numberField(vkRaw, "power")], "w") : scalarToString(curve, vk.w, "w"),
X_2: pointObjectToBlstCHex(curve.G2, vk.X_2, "X_2")
};
for (const key of G1_KEYS) {
res[key] = pointObjectToBlstCHex(curve.G1, vk[key], key);
res[`${key}_uc`] = pointObjectToUncompressedHex(curve.G1, vk[key], key);
}
return stringifyBigInts(res);
} finally {
if (typeof curve.terminate === "function") {
await curve.terminate();
}
}
}
function isTemplateReadyPlonkVerificationKey(vkRaw) {
return G1_KEYS.every((key) => typeof vkRaw[key] === "string") && G1_UC_KEYS.every((key) => typeof vkRaw[key] === "string") && typeof vkRaw.X_2 === "string";
}
function numberField(vkRaw, key) {
const value = Number(vkRaw[key]);
if (!Number.isInteger(value) || value < 0) {
throw new Error(`verification_key: invalid '${key}'.`);
}
return value;
}
function scalarToString(curve, value, key) {
if (value === void 0 || value === null) {
throw new Error(`verification_key: missing '${key}'.`);
}
return curve.Fr.toObject(curve.Fr.fromObject(value)).toString();
}
function ffC2blstC(a, offset = 0) {
if (a[offset] & 128) {
a[offset] |= 32;
}
a[offset] |= 128;
}
function pointObjectToBlstCHex(curve, p, key) {
if (p === void 0 || p === null) {
throw new Error(`verification_key: missing '${key}'.`);
}
return pointToBlstCHex(curve, curve.fromObject(p));
}
function pointToBlstCHex(curve, p) {
const tmp = new Uint8Array(curve.F.n8);
curve.toRprCompressed(tmp, 0, p);
ffC2blstC(tmp, 0);
return Buffer.from(tmp).toString("hex");
}
function pointObjectToUncompressedHex(curve, p, key) {
if (p === void 0 || p === null) {
throw new Error(`verification_key: missing '${key}'.`);
}
return pointToUncompressedHex(curve, curve.fromObject(p));
}
function pointToUncompressedHex(curve, p) {
const tmp = new Uint8Array(curve.F.n8 * 2);
curve.toRprUncompressed(tmp, 0, p);
return Buffer.from(tmp).toString("hex");
}
async function plonkVk(zkey) {
const curve = zkey.curve;
try {
assertPlonkPublicInputCount(zkey.nPublic, "verification_key.nPublic");
let vKey = {
protocol: zkey.protocol,
curve: curve.name,
nPublic: zkey.nPublic,
power: zkey.power,
k1: curve.Fr.toObject(zkey.k1),
k2: curve.Fr.toObject(zkey.k2),
Qm: pointToBlstCHex(curve.G1, zkey.Qm),
Ql: pointToBlstCHex(curve.G1, zkey.Ql),
Qr: pointToBlstCHex(curve.G1, zkey.Qr),
Qo: pointToBlstCHex(curve.G1, zkey.Qo),
Qc: pointToBlstCHex(curve.G1, zkey.Qc),
S1: pointToBlstCHex(curve.G1, zkey.S1),
S2: pointToBlstCHex(curve.G1, zkey.S2),
S3: pointToBlstCHex(curve.G1, zkey.S3),
Qm_uc: pointToUncompressedHex(curve.G1, zkey.Qm),
Ql_uc: pointToUncompressedHex(curve.G1, zkey.Ql),
Qr_uc: pointToUncompressedHex(curve.G1, zkey.Qr),
Qo_uc: pointToUncompressedHex(curve.G1, zkey.Qo),
Qc_uc: pointToUncompressedHex(curve.G1, zkey.Qc),
S1_uc: pointToUncompressedHex(curve.G1, zkey.S1),
S2_uc: pointToUncompressedHex(curve.G1, zkey.S2),
S3_uc: pointToUncompressedHex(curve.G1, zkey.S3),
X_2: pointToBlstCHex(curve.G2, zkey.X_2),
w: curve.Fr.toObject(curve.Fr.w[zkey.power])
};
vKey = stringifyBigInts(vKey);
return vKey;
} finally {
if (typeof curve.terminate === "function") {
await curve.terminate();
}
}
}
// src/formats/snarkjs.js
var SNARKJS_JSON_SOURCE = "snarkjs-json";
var SNARKJS_ZKEY_SOURCE = "snarkjs-zkey";
var GROTH16_REQUIRED_FIELDS = [
"vk_alpha_1",
"vk_beta_2",
"vk_gamma_2",
"vk_delta_2"
];
var PLONK_REQUIRED_FIELDS = [
"nPublic",
"power",
"k1",
"k2",
"Qm",
"Ql",
"Qr",
"Qo",
"Qc",
"S1",
"S2",
"S3",
"X_2"
];
function normalizeSnarkjsVerificationKey(vkRaw) {
if (!(vkRaw == null ? void 0 : vkRaw.protocol)) {
throw new Error("verification_key: missing 'protocol'.");
}
if (!vkRaw.curve) {
throw new Error("verification_key: missing 'curve'.");
}
ensureSupportedProtocol(vkRaw.protocol, "JSON");
validateSnarkjsVerificationKeyShape(vkRaw);
return vkRaw;
}
async function loadSnarkjsZkeyVerificationKey(inputPath, { logger = null } = {}) {
var _a;
(_a = logger == null ? void 0 : logger.log) == null ? void 0 : _a.call(logger, "Loading verification key from .zkey...");
const vkRaw = await zKey.exportVerificationKey(inputPath);
ensureSupportedProtocol(vkRaw.protocol, ".zkey");
return vkRaw;
}
async function loadSnarkjsPlonkTemplateVerificationKey(verifierInput) {
if (verifierInput.sourceFormat === SNARKJS_ZKEY_SOURCE) {
return zkeyExportPlonkVerificationKey(verifierInput.inputPath);
}
return normalizePlonkVerificationKey(verifierInput.verificationKey);
}
function ensureSupportedProtocol(protocol, source) {
if (protocol !== "groth16" && protocol !== "plonk") {
throw new Error(
`Only Groth16 and PLONK are supported from ${source} (got '${protocol}').`
);
}
}
function validateSnarkjsVerificationKeyShape(vkRaw) {
if (vkRaw.protocol === "groth16") {
requireFields(vkRaw, GROTH16_REQUIRED_FIELDS);
if (!Array.isArray(vkRaw.IC) || vkRaw.IC.length === 0) {
throw new Error("verification_key: 'IC' must be a non-empty array.");
}
return;
}
requireFields(vkRaw, PLONK_REQUIRED_FIELDS);
}
function requireFields(value, fields) {
for (const field of fields) {
if (value[field] === void 0 || value[field] === null) {
throw new Error(`verification_key: missing '${field}'.`);
}
}
}
// src/formats/gnark.js
import fs3 from "fs/promises";
// src/formats/common.js
import fs2 from "fs/promises";
import { buildBls12381 as buildBls123814 } from "ffjavascript";
var BLS12381_CURVE_NAME = "bls12381";
function normalizeBls12381CurveName(raw, field = "curve") {
if (typeof raw !== "string" || raw.trim() === "") {
throw new Error(`${field}: BLS12-381 curve metadata is required.`);
}
const normalized = raw.toLowerCase().replace(/[^a-z0-9]/g, "");
if (normalized !== "bls12381") {
throw new Error(
`${field}: only BLS12-381 artifacts are supported for gnark/arkworks inputs (got '${raw}').`
);
}
return BLS12381_CURVE_NAME;
}
function decimalString(value, field) {
if (typeof value === "bigint") return value.toString();
if (typeof value === "number" && Number.isInteger(value)) return String(value);
if (typeof value === "string") {
const trimmed = value.trim();
if (/^[0-9]+$/.test(trimmed)) return trimmed;
}
throw new Error(`${field} must be a decimal string or integer.`);
}
function requireField(value, field) {
if (value === void 0 || value === null) {
throw new Error(`verification key: missing '${field}'.`);
}
return value;
}
function g1Point(x, y, field) {
return [
decimalString(x, `${field}.X`),
decimalString(y, `${field}.Y`),
"1"
];
}
function g2Point(x0, x1, y0, y1, field) {
return [
[
decimalString(x0, `${field}.X.A0`),
decimalString(x1, `${field}.X.A1`)
],
[
decimalString(y0, `${field}.Y.A0`),
decimalString(y1, `${field}.Y.A1`)
],
["1", "0"]
];
}
function makeGroth16VerificationKey({
curve = BLS12381_CURVE_NAME,
vk_alpha_1,
vk_beta_2,
vk_gamma_2,
vk_delta_2,
IC
}) {
if (!Array.isArray(IC) || IC.length === 0) {
throw new Error("verification key: IC/G1.K must contain at least one point.");
}
return {
protocol: "groth16",
curve,
vk_alpha_1,
vk_beta_2,
vk_gamma_2,
vk_delta_2,
IC,
nPublic: IC.length - 1
};
}
async function readJson(path4, label) {
const content = await fs2.readFile(path4, "utf8");
try {
return JSON.parse(content);
} catch (err) {
throw new Error(`invalid ${label} JSON in ${path4}: ${err.message}`);
}
}
function decodeHex(raw, field) {
if (typeof raw !== "string") {
throw new Error(`${field} must be a hex string.`);
}
const hex = raw.trim().replace(/^0x/i, "");
if (hex.length === 0) throw new Error(`${field} must not be empty.`);
if (hex.length % 2 !== 0) {
throw new Error(`${field} has odd hex length.`);
}
if (!/^[0-9a-fA-F]+$/.test(hex)) {
throw new Error(`${field} must be a hex string.`);
}
return Uint8Array.from(Buffer.from(hex, "hex"));
}
async function withBls12381(run) {
const curve = await buildBls123814();
try {
return await run(curve);
} finally {
if (curve && typeof curve.terminate === "function") {
await curve.terminate();
}
}
}
function decodeBls12381G1(curve, bytes, field) {
const point = decodeBls12381Point(curve.G1, bytes, 48, field);
return curvePointToObjectArray(curve.G1, point);
}
function decodeBls12381G2(curve, bytes, field) {
const point = decodeBls12381Point(curve.G2, bytes, 96, field);
return curvePointToObjectArray(curve.G2, point);
}
function curvePointToObjectArray(curveGroup, point) {
return stringifyBigIntsDeep(curveGroup.toObject(point));
}
function stringifyBigIntsDeep(value) {
if (typeof value === "bigint") return value.toString();
if (Array.isArray(value)) return value.map(stringifyBigIntsDeep);
return value;
}
function decodeBls12381Point(group, bytes, compressedSize, field) {
const first = bytes[0];
if (first === void 0) {
throw new Error(`${field}: empty point encoding.`);
}
const flag = first & 224;
const compressed = flag === 128 || flag === 160 || flag === 192;
const uncompressed = flag === 0 || flag === 64;
if (!compressed && !uncompressed) {
throw new Error(
`${field}: unsupported gnark/arkworks point flag 0x${flag.toString(16)}.`
);
}
if (compressed) {
if (bytes.length !== compressedSize) {
throw new Error(
`${field}: expected ${compressedSize} compressed bytes, got ${bytes.length}.`
);
}
if (flag === 192) return group.zero;
return group.fromRprCompressed(toFfjavascriptCompressed(bytes), 0);
}
if (bytes.length !== compressedSize * 2) {
throw new Error(
`${field}: expected ${compressedSize * 2} uncompressed bytes, got ${bytes.length}.`
);
}
if (flag === 64) return group.zero;
return group.fromRprUncompressed(bytes, 0);
}
function toFfjavascriptCompressed(bytes) {
const converted = Uint8Array.from(bytes);
const sign = converted[0] & 32;
converted[0] &= 31;
if (sign) converted[0] |= 128;
return converted;
}
var BinaryReader = class {
constructor(bytes, sourceName) {
this.bytes = bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes);
this.sourceName = sourceName;
this.offset = 0;
}
readBytes(len, field) {
const end = this.offset + len;
if (end > this.bytes.length) {
throw new Error(
`${this.sourceName} ended while reading ${field}: need ${len} bytes at offset ${this.offset}, remaining ${this.bytes.length - this.offset}.`
);
}
const slice = this.bytes.slice(this.offset, end);
this.offset = end;
return slice;
}
peekByte(field) {
const value = this.bytes[this.offset];
if (value === void 0) {
throw new Error(`${this.sourceName} ended before reading ${field}.`);
}
return value;
}
readU32BE(field) {
const bytes = this.readBytes(4, field);
return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(
0,
false
);
}
readU64BE(field) {
const bytes = this.readBytes(8, field);
return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getBigUint64(
0,
false
);
}
readU64LE(field) {
const bytes = this.readBytes(8, field);
return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getBigUint64(
0,
true
);
}
finish(label = "artifact") {
if (this.offset !== this.bytes.length) {
throw new Error(
`${this.sourceName} has ${this.bytes.length - this.offset} trailing bytes after ${label}.`
);
}
}
};
// src/formats/gnark.js
var BLS_FIELD_BYTES = 48;
var GNARK_FLAG_MASK = 224;
var GNARK_COMPRESSED_FLAGS = /* @__PURE__ */ new Set([128, 160, 192]);
var GNARK_UNCOMPRESSED_FLAGS = /* @__PURE__ */ new Set([0, 64]);
async function normalizeGnarkJsonVerificationKey(value) {
const g1 = requireField(value == null ? void 0 : value.G1, "G1");
const g2 = requireField(value == null ? void 0 : value.G2, "G2");
ensureNoVerificationKeyCommitments(value);
const k = requireField(g1.K, "G1.K");
if (!Array.isArray(k)) {
throw new Error("verification key: G1.K must be an array.");
}
const verificationKey = makeGroth16VerificationKey({
curve: BLS12381_CURVE_NAME,
vk_alpha_1: g1FromJson(requireField(g1.Alpha, "G1.Alpha"), "G1.Alpha"),
vk_beta_2: g2FromJson(requireField(g2.Beta, "G2.Beta"), "G2.Beta"),
vk_gamma_2: g2FromJson(requireField(g2.Gamma, "G2.Gamma"), "G2.Gamma"),
vk_delta_2: g2FromJson(requireField(g2.Delta, "G2.Delta"), "G2.Delta"),
IC: k.map((point, idx) => g1FromJson(point, `G1.K[${idx}]`))
});
await assertBls12381VerificationKeyPoints(verificationKey);
return verificationKey;
}
async function loadGnarkBinaryVerificationKey(path4) {
const bytes = await fs3.readFile(path4);
return normalizeGnarkBinaryVerificationKey(Uint8Array.from(bytes), path4);
}
async function normalizeGnarkBinaryVerificationKey(bytes, sourceName = "gnark binary VK") {
return withBls12381((curve) => {
const reader = new BinaryReader(bytes, sourceName);
const alpha = readG12(reader, curve, "G1.Alpha");
readG12(reader, curve, "G1.Beta");
const beta = readG22(reader, curve, "G2.Beta");
const gamma = readG22(reader, curve, "G2.Gamma");
readG12(reader, curve, "G1.Delta");
const delta = readG22(reader, curve, "G2.Delta");
const icLen = reader.readU32BE("G1.K length");
const IC = [];
for (let idx = 0; idx < icLen; idx += 1) {
IC.push(readG12(reader, curve, `G1.K[${idx}]`));
}
const publicAndCommitmentCommitted = readU64SliceSlice(
reader,
"PublicAndCommitmentCommitted"
);
const commitmentKeysLen = reader.readU32BE("CommitmentKeys length");
if (publicAndCommitmentCommitted.length !== 0 || commitmentKeysLen !== 0) {
throw new Error(
"gnark commitment keys are not supported by TON verifier generation."
);
}
reader.finish("gnark verification key");
return makeGroth16VerificationKey({
curve: BLS12381_CURVE_NAME,
vk_alpha_1: alpha,
vk_beta_2: beta,
vk_gamma_2: gamma,
vk_delta_2: delta,
IC
});
});
}
function ensureNoVerificationKeyCommitments(value) {
const commitmentKeys = value == null ? void 0 : value.CommitmentKeys;
const publicAndCommitmentCommitted = value == null ? void 0 : value.PublicAndCommitmentCommitted;
if (!isEmptyCommitmentValue(commitmentKeys) || !isEmptyCommitmentValue(publicAndCommitmentCommitted)) {
throw new Error(
"gnark commitment keys are not supported by TON verifier generation."
);
}
}
function isEmptyCommitmentValue(value) {
return value === void 0 || value === null || Array.isArray(value) && value.length === 0;
}
function g1FromJson(point, field) {
return g1Point(
requireField(point == null ? void 0 : point.X, `${field}.X`),
requireField(point == null ? void 0 : point.Y, `${field}.Y`),
field
);
}
function g2FromJson(point, field) {
var _a, _b, _c, _d;
return g2Point(
requireField((_a = point == null ? void 0 : point.X) == null ? void 0 : _a.A0, `${field}.X.A0`),
requireField((_b = point == null ? void 0 : point.X) == null ? void 0 : _b.A1, `${field}.X.A1`),
requireField((_c = point == null ? void 0 : point.Y) == null ? void 0 : _c.A0, `${field}.Y.A0`),
requireField((_d = point == null ? void 0 : point.Y) == null ? void 0 : _d.A1, `${field}.Y.A1`),
field
);
}
async function assertBls12381VerificationKeyPoints(vk) {
await withBls12381((curve) => {
assertBls12381Point(curve.G1, vk.vk_alpha_1, "G1.Alpha");
assertBls12381Point(curve.G2, vk.vk_beta_2, "G2.Beta");
assertBls12381Point(curve.G2, vk.vk_gamma_2, "G2.Gamma");
assertBls12381Point(curve.G2, vk.vk_delta_2, "G2.Delta");
vk.IC.forEach((point, idx) => {
assertBls12381Point(curve.G1, point, `G1.K[${idx}]`);
});
});
}
function assertBls12381Point(group, point, field) {
let parsed;
try {
parsed = group.fromObject(bigintPoint(point));
} catch (err) {
throw new Error(
`${field}: expected a valid BLS12-381 point (${(err == null ? void 0 : err.message) || err}).`
);
}
if (!group.isValid(parsed)) {
throw new Error(`${field}: point is not on BLS12-381.`);
}
}
function bigintPoint(value) {
if (Array.isArray(value)) return value.map(bigintPoint);
if (typeof value === "bigint") return value;
return BigInt(value);
}
function readG12(reader, curve, field) {
const bytes = reader.readBytes(pointLength(reader, BLS_FIELD_BYTES, field), field);
return decodeBls12381G1(curve, bytes, field);
}
function readG22(reader, curve, field) {
const bytes = reader.readBytes(
pointLength(reader, BLS_FIELD_BYTES * 2, field),
field
);
return decodeBls12381G2(curve, bytes, field);
}
function pointLength(reader, compressedSize, field) {
const flag = reader.peekByte(field) & GNARK_FLAG_MASK;
if (GNARK_COMPRESSED_FLAGS.has(flag)) return compressedSize;
if (GNARK_UNCOMPRESSED_FLAGS.has(flag)) return compressedSize * 2;
throw new Error(
`${field}: unsupported gnark point flag 0x${flag.toString(16).padStart(2, "0")}.`
);
}
function readU64SliceSlice(reader, field) {
const outerLen = reader.readU32BE(`${field} length`);
const outer = [];
for (let outerIdx = 0; outerIdx < outerLen; outerIdx += 1) {
const innerLen = reader.readU32BE(`${field}[${outerIdx}] length`);
const inner = [];
for (let innerIdx = 0; innerIdx < innerLen; innerIdx += 1) {
inner.push(reader.readU64BE(`${field}[${outerIdx}][${innerIdx}]`));
}
outer.push(inner);
}
return outer;
}
// src/formats/arkworks.js
var G1_COMPRESSED_BYTES = 48;
var G2_COMPRESSED_BYTES = 96;
async function normalizeArkworksVerificationKey(value) {
const curve = parseCurve(value);
const vkHex = firstString(value, ["vk", "verification_key", "verifying_key"], "vk");
return decodeArkworksVerificationKey(vkHex, curve);
}
async function decodeArkworksVerificationKey(raw, curveName = BLS12381_CURVE_NAME) {
normalizeBls12381CurveName(curveName, "curve");
const bytes = decodeHex(raw, "vk");
return withBls12381((curve) => {
const reader = new BinaryReader(bytes, "arkworks verification key");
const vk_alpha_1 = decodeBls12381G1(
curve,
reader.readBytes(G1_COMPRESSED_BYTES, "vk.alpha_g1"),
"vk.alpha_g1"
);
const vk_beta_2 = decodeBls12381G2(
curve,
reader.readBytes(G2_COMPRESSED_BYTES, "vk.beta_g2"),
"vk.beta_g2"
);
const vk_gamma_2 = decodeBls12381G2(
curve,
reader.readBytes(G2_COMPRESSED_BYTES, "vk.gamma_g2"),
"vk.gamma_g2"
);
const vk_delta_2 = decodeBls12381G2(
curve,
reader.readBytes(G2_COMPRESSED_BYTES, "vk.delta_g2"),
"vk.delta_g2"
);
const icLen = Number(reader.readU64LE("vk.gamma_abc_g1 length"));
if (!Number.isSafeInteger(icLen)) {
throw new Error("vk.gamma_abc_g1 length is too large.");
}
const IC = [];
for (let idx = 0; idx < icLen; idx += 1) {
IC.push(
decodeBls12381G1(
curve,
reader.readBytes(G1_COMPRESSED_BYTES, `vk.gamma_abc_g1[${idx}]`),
`vk.gamma_abc_g1[${idx}]`
)
);
}
reader.finish("arkworks verification key");
return makeGroth16VerificationKey({
curve: BLS12381_CURVE_NAME,
vk_alpha_1,
vk_beta_2,
vk_gamma_2,
vk_delta_2,
IC
});
});
}
function parseCurve(value) {
const curve = value == null ? void 0 : value.curve;
if (!curve) {
throw new Error("arkworks input requires curve metadata.");
}
return normalizeBls12381CurveName(curve, "curve");
}
function firstString(value, keys, label) {
for (const key of keys) {
const candidate = value == null ? void 0 : value[key];
if (typeof candidate === "string") return candidate;
}
throw new Error(`arkworks input requires ${label} hex field.`);
}
// src/formats/index.js
var GNARK_JSON_SOURCE = "gnark-json";
var GNARK_BINARY_SOURCE = "gnark-bin";
var ARKWORKS_JSON_SOURCE = "arkworks-json";
async function loadVerificationKey(inputPath, { logger = null, quiet = false } = {}) {
const log = createLogger({ logger, quiet });
const nativeInput = await loadNativeVerificationKeyInput(inputPath);
if (nativeInput) return nativeInput;
return makeVerifierInput(
inputPath,
SNARKJS_ZKEY_SOURCE,
await loadSnarkjsZkeyVerificationKey(inputPath, { logger: log })
);
}
async function loadPlonkTemplateVerificationKey(verifierInput) {
return loadSnarkjsPlonkTemplateVerificationKey(verifierInput);
}
async function loadNativeVerificationKeyInput(inputPath) {
const lower = inputPath.toLowerCase();
if (lower.endsWith(".json")) {
return loadJsonVerificationKey(inputPath);
}
if (lower.endsWith(".bin")) {
return makeVerifierInput(
inputPath,
GNARK_BINARY_SOURCE,
await loadGnarkBinaryVerificationKey(inputPath)
);
}
return null;
}
async function loadJsonVerificationKey(inputPath) {
const raw = await readJson(inputPath, "verification key");
const errors = [];
try {
return makeVerifierInput(
inputPath,
SNARKJS_JSON_SOURCE,
normalizeSnarkjsVerificationKey(raw)
);
} catch (err) {
errors.push(`snarkjs JSON: ${err.message}`);
}
try {
return makeVerifierInput(
inputPath,
GNARK_JSON_SOURCE,
await normalizeGnarkJsonVerificationKey(raw)
);
} catch (err) {
errors.push(`gnark JSON: ${err.message}`);
}
try {
return makeVerifierInput(
inputPath,
ARKWORKS_JSON_SOURCE,
await normalizeArkworksVerificationKey(raw)
);
} catch (err) {
errors.push(`arkworks JSON: ${err.message}`);
}
throw new Error(
`could not auto-detect verification key JSON format: ${errors.join("; ")}`
);
}
function makeVerifierInput(inputPath, sourceFormat, verificationKey) {
return {
inputPath,
sourceFormat,
verificationKey
};
}
// src/generateVerifiers.js
var { unstringifyBigInts: unstringifyBigInts2 } = utils3;
async function generateVerifier(inputPath, outputPath, {
lang = "tolk",
templatesDir = defaultTemplatesDir(),
contractName = null,
logger = null,
quiet = false
} = {}) {
const log = createLogger({ logger, quiet });
const verifierInput = await loadVerificationKey(inputPath, { logger: log });
const vkRaw = verifierInput.verificationKey;
assertTonBlsCurve(vkRaw.curve, "verification_key.curve");
const normalizedContractName = typeof contractName === "string" && contractName.trim() ? normalizeContractName(contractName) : null;
const templatePath = await resolveTemplatePath(
templatesDir,
lang,
vkRaw.protocol
);
log.log(`Using template for: ${lang} ${vkRaw.protocol}`);
let curve;
try {
if (vkRaw.protocol === "groth16") {
const vk = unstringifyBigInts2(vkRaw);
curve = await getCurveFromName(vkRaw.curve);
log.log("Compressing points...");
const data = {
protocol: "groth16",
curve: vkRaw.curve,
contractName: normalizedContractName,
vk_alpha_1: g1Compressed(curve, vk.vk_alpha_1),
vk_beta_2: g2Compressed(curve, vk.vk_beta_2),
vk_gamma_2: g2Compressed(curve, vk.vk_gamma_2),
vk_delta_2: g2Compressed(curve, vk.vk_delta_2),
IC: vk.IC.map((x) => g1Compressed(curve, x)),
nPublic: vk.IC.length - 1,
publicInputKeyLen: 32,
_raw: vkRaw
};
log.log("Rendering template...");
const template2 = await fs4.readFile(templatePath, "utf8");
const rendered2 = ejs.render(template2, data);
log.log(`Saving file: ${outputPath}`);
await fs4.writeFile(outputPath, rendered2, "utf8");
log.log("Done.");
return "groth16";
}
log.log("Rendering template for PLONK...");
const template = await fs4.readFile(templatePath, "utf8");
const verificationKey = {
...await loadPlonkTemplateVerificationKey(verifierInput),
contractName: normalizedContractName
};
const rendered = ejs.render(template, verificationKey);
log.log(`Saving file: ${outputPath}`);
await fs4.writeFile(outputPath, rendered, "utf8");
log.log("Done.");
return "plonk";
} finally {
if (curve && typeof curve.terminate === "function") {
await curve.terminate();
}
}
}
// src/export_plonk_calldata.js
import { utils as utils4 } from "ffjavascript";
import { beginCell, Cell, Dictionary as Dictionary2 } from "@ton/core";
var { unstringifyBigInts: unstringifyBigInts3 } = utils4;
function calldataToTupleItems(calldata) {
return calldata.map((item) => {
if ((item.type === "slice" || item.type === "cell") && item.cell != null) {
const c = item.cell;
if (typeof c.toBoc === "function") {
const ourCell = Cell.fromBoc(c.toBoc())[0];
return { type: item.type, cell: ourCell };
}
}
return item;
});
}
async function exportPlonkFuncCalldata(_proof, _pub) {
return exportPlonkCalldataWithPublicInputs(_proof, _pub, funcPublicInputs);
}
async function exportPlonkTolkCalldata(_proof, _pub) {
return exportPlonkCalldataWithPublicInputs(_proof, _pub, tolkPublicInputs);
}
async function exportPlonkCalldataWithPublicInputs(_proof, _pub, publicInputItem) {
let proof = unstringifyBigInts3(_proof);
const pub = unstringifyBigInts3(_pub);
if (!Array.isArray(pub)) {
throw new Error("public inputs must be an array.");
}
assertPlonkPublicInputCount(pub.length, "public inputs");
const curve = await getCurveFromName2(proof.curve);
try {
proof = fromObjectProof(curve, proof);
} finally {
if (typeof curve.terminate === "function") {
await curve.terminate();
}
}
const hexToCell = (hex) => beginCell().storeBuffer(Buffer.from(hex, "hex")).endCell();
const calldata = [
{
type: "slice",
cell: beginCell().storeBuffer(Buffer.from(proof.A, "hex")).endCell()
},
{
type: "slice",
cell: beginCell().storeBuffer(Buffer.from(proof.B, "hex")).endCell()
},
{
type: "slice",
cell: beginCell().storeBuffer(Buffer.from(proof.C, "hex")).endCell()
},
{
type: "slice",
cell: beginCell().storeBuffer(Buffer.from(proof.Z, "hex")).endCell()
},
{
type: "slice",
cell: beginCell().storeBuffer(Buffer.from(proof.T1, "hex")).endCell()
},
{
type: "slice",
cell: beginCell().storeBuffer(Buffer.from(proof.T2, "hex")).endCell()
},
{
type: "slice",
cell: beginCell().storeBuffer(Buffer.from(proof.T3, "hex")).endCell()
},
{ type: "int", value: proof.eval_a },
{ type: "int", value: proof.eval_b },
{ type: "int", value: proof.eval_c },
{ type: "int", value: proof.eval_s1 },
{ type: "int", value: proof.eval_s2 },
{ type: "int", value: proof.eval_zw },
{
type: "slice",
cell: beginCell().storeBuffer(Buffer.from(proof.Wxi, "hex")).endCell()
},
{
type: "slice",
cell: beginCell().storeBuffer(Buffer.from(proof.Wxiw, "hex")).endCell()
},
publicInputItem(pub),
{
type: "slice",
cell: hexToCell(proof.A_uc)
},
{
type: "slice",
cell: hexToCell(proof.B_uc)
},
{
type: "slice",
cell: hexToCell(proof.C_uc)
},
{
type: "slice",
cell: hexToCell(proof.Z_uc)
},
{
type: "slice",
cell: hexToCell(proof.T1_uc)
},
{
type: "slice",
cell: hexToCell(proof.T2_uc)
},
{
type: "slice",
cell: hexToCell(proof.T3_uc)
},
{
type: "slice",
cell: hexToCell(proof.Wxi_uc)
},
{
type: "slice",
cell: hexToCell(proof.Wxiw_uc)
}
];
return calldataToTupleItems(calldata);
}
function funcPublicInputs(vs) {
const d = Dictionary2.empty(
Dictionary2.Keys.Uint(32),
Dictionary2.Values.BigUint(256)
);
for (let i = 0; i < vs.length; i++) {
d.set(i, vs[i]);
}
const b = beginCell();
d.storeDirect(b);
return {
type: "cell",
cell: b.endCell()
};
}
function tolkPublicInputs(vs) {
return {
type: "tuple",
items: vs.map((value) => ({ type: "int", value }))
};
}
function ffC2blstC2(a, offset = 0) {
if (a[offset] & 128) {
a[offset] |= 32;
}
a[offset] |= 128;
}
function pointToBlstCHex2(curve, p) {
const tmp = new Uint8Array(curve.F.n8);
curve.toRprCompressed(tmp, 0, p);
ffC2blstC2(tmp, 0);
return Buffer.from(tmp).toString("hex");
}
function pointToUncompressedHex2(curve, p) {
const tmp = new Uint8Array(curve.F.n8 * 2);
curve.toRprUncompressed(tmp, 0, p);
return Buffer.from(tmp).toString("hex");
}
function fromObjectProof(curve, proof) {
const G1 = curve.G1;
const Fr = curve.Fr;
const res = {};
const A = G1.fromObject(proof.A);
const B = G1.fromObject(proof.B);
const C = G1.fromObject(proof.C);
const Z = G1.fromObject(proof.Z);
const T1 = G1.fromObject(proof.T1);
const T2 = G1.fromObject(proof.T2);
const T3 = G1.fromObject(proof.T3);
const Wxi = G1.fromObject(proof.Wxi);
const Wxiw = G1.fromObject(proof.Wxiw);
res.A = pointToBlstCHex2(curve.G1, A);
res.B = pointToBlstCHex2(curve.G1, B);
res.C = pointToBlstCHex2(curve.G1, C);
res.Z = pointToBlstCHex2(curve.G1, Z);
res.T1 = pointToBlstCHex2(curve.G1, T1);
res.T2 = pointToBlstCHex2(curve.G1, T2);
res.T3 = pointToBlstCHex2(curve.G1, T3);
res.Wxi = pointToBlstCHex2(curve.G1, Wxi);
res.Wxiw = pointToBlstCHex2(curve.G1, Wxiw);
res.A_uc = pointToUncompressedHex2(curve.G1, A);
res.B_uc = pointToUncompressedHex2(curve.G1, B);
res.C_uc = pointToUncompressedHex2(curve.G1, C);
res.Z_uc = pointToUncompressedHex2(curve.G1, Z);
res.T1_uc = pointToUncompressedHex2(curve.G1, T1);
res.T2_uc = pointToUncompressedHex2(curve.G1, T2);
res.T3_uc = pointToUncompressedHex2(curve.G1, T3);
res.Wxi_uc = pointToUncompressedHex2(curve.G1, Wxi);
res.Wxiw_uc = pointToUncompressedHex2(curve.G1, Wxiw);
res.eval_a = Fr.toObject(Fr.fromObject(proof.eval_a));
res.eval_b = Fr.toObject(Fr.fromObject(proof.eval_b));
res.eval_c = Fr.toObject(Fr.fromObject(proof.eval_c));
res.eval_s1 = Fr.toObject(Fr.fromObject(proof.eval_s1));
res.eval_s2 = Fr.toObject(Fr.fromObject(proof.eval_s2));
res.eval_zw = Fr.toObject(Fr.fromObject(proof.eval_zw));
return res;
}
// src/artifactInfo.js
import path3 from "path";
function check(name, run) {
try {
const message = run();
return { name, ok: true, message };
} catch (err) {
return { name, ok: false, message: (err == null ? void 0 : err.message) || String(err) };
}
}
function publicInputCount(vkRaw) {
if (vkRaw.protocol === "groth16") {
return Array.isArray(vkRaw.IC) ? vkRaw.IC.length - 1 : null;
}
if (vkRaw.protocol === "plonk") {
return Number(vkRaw.nPublic);
}
return null;
}
async function inspectVerifierInput(inputPath, { lang = "tolk", templatesDir = defaultTemplatesDir() } = {}) {
const verifierInput = await loadVerificationKey(inputPath);
const vkRaw = verifierInput.verificationKey;
const nPublic = publicInputCount(vkRaw);
const checks = [];
checks.push(
check("curve", () => {
assertTonBlsCurve(vkRaw.curve, "verification_key.curve");
return "BLS12-381 is supported by TON BLS opcodes.";
})
);
if (vkRaw.protocol === "plonk") {
checks.push(
check("plonk-public-inputs", () => {
assertPlonkPublicInputCount(nPublic, "verification_key.nPublic");
return "PLONK public input count fits TVM Keccak tuple limits.";
})
);
}
let template = null;
checks.push(
await (async () => {
try {
template = await resolveTemplatePath(templatesDir, lang, vkRaw.protocol);
return {
name: "template",
ok: true,
message: `Template found: ${path3.basename(template)}.`
};
} catch (err) {
return {
name: "template",
ok: false,
message: (err == null ? void 0 : err.message) || String(err)
};
}
})()
);
const ok = checks.every((item) => item.ok);
return {
ok,
inputPath,
sourceFormat: verifierInput.sourceFormat,
protocol: vkRaw.protocol,
curve: vkRaw.curve,
nPublic,
language: lang,
template,
checks
};
}
function formatDoctorReport(report) {
const lines = [
`Verifier doctor: ${report.ok ? "OK" : "BLOCKED"}`,
`Input: ${report.inputPath}`,
`Source format: ${report.sourceFormat}`,
`Protocol: ${report.protocol}`,
`Curve: ${report.curve}`,
`Public inputs: ${report.nPublic}`,
`Language: ${report.language}`,
`Template: ${report.template ?? "not found"}`,
"Checks:"
];
for (const item of report.checks) {
lines.push(` [${item.ok ? "ok" : "blocked"}] ${item.name}: ${item.message}`);
}
return `${lines.join("\n")}
`;
}
// src/proofMessages.js
import { beginCell as beginCell2, Cell as Cell2, Dictionary as Dictionary3 } from "@ton/core";
var GROTH16_VERIFY_OP = 993839639;
var PLONK_VERIFY_PROOF_OP = 1985103449;
var UINT256_ARRAY_CHUNK_SIZE = 3;
function normalizeProtocol(proof, protocol) {
const detected = protocol ?? (proof == null ? void 0 : proof.protocol);
if (detected !== "groth16" && detected !== "plonk") {
throw new Error("proof protocol must be 'groth16' or 'plonk'.");
}
return detected;
}
function normalizeLang(lang = "tolk") {
if (lang !== "tolk" && lang !== "func") {
throw new Error("proof-to-message supports --tolk and --func.");
}
return lang;
}
function serializeTolkIntArray(list) {
if (list.length > 255) {
throw new Error("Tolk array<int> wrapper supports at most 255 items.");
}
let tail = null;
for (let i = list.length; i > 0; i -= 3) {
const chunk = list.slice(Math.max(0, i - 3), i);
const cell = beginCell2();
for (const value of chunk) {
cell.storeInt(value, 257);
}
if (tail) {
cell.storeRef(tail);
}
tail = cell.endCell();
}
const root = beginCell2().storeUint(list.length, 8);
if (tail) {
root.storeSlice(tail.beginParse());
}
return root.endCell();
}
function groth16FuncPublicInputs(list) {
const dict = Dictionary3.empty(
Dictionary3.Keys.BigUint(32),
Dictionary3.Values.BigInt(256)
);
for (let i = 0; i < list.length; i += 1) {
dict.set(BigInt(i), list[i]);
}
return dict;
}
async function groth16MessageCell(proof, publicSignals, lang) {
const compressed = await groth16CompressProof(proof, publicSignals);
const piA = beginCell2().storeBuffer(compressed.pi_a).endCell();
const piB = beginCell2().storeBuffer(compressed.pi_b).endCell();
const piC = beginCell2().storeBuffer(compressed.pi_c).endCell();
const body = beginCell2().storeUint(GROTH16_VERIFY_OP, 32).storeRef(piA).storeRef(piB).storeRef(piC);
if (lang === "tolk") {
body.storeSlice(serializeTolkIntArray(compressed.pubInputs).beginParse());
} else {
body.storeDict(groth16FuncPublicInputs(compressed.pubInputs));
}
return body.endCell();
}
function requireTupleCell(args, index, type = "slice") {
const item = args[index];
if ((item == null ? void 0 : item.type) !== type) {
throw new Error(`Expected calldata[${index}] to be ${type}.`);
}
return item.cell;
}
function requireTupleInt(args, index) {
const item = args[index];
if ((item == null ? void 0 : item.type) !== "int") {
throw new Error(`Expected calldata[${index}] to be int.`);
}
return item.value;
}
function requirePublicInputTuple(args) {
const item = args[15];
if ((item == null ? void 0 : item.type) !== "tuple") {
throw new Error("Expected PLONK Tolk calldata[15] to be tuple.");
}
return item.items.map((publicInput, index) => {
if (publicInput.type !== "int") {
throw new Error(`Expected PLONK public input ${index} to be int.`);
}
return publicInput.value;
});
}
function uint256ArrayToCell(values) {
const chunks = [];
for (let i = 0; i < values.length; i += UINT256_ARRAY_CHUNK_SIZE) {
chunks.push(values.slice(i, i + UINT256_ARRAY_CHUNK_SIZE));
}
const buildChunk = (index) => {
const hasNext = index + 1 < chunks.length;
const builder2 = beginCell2().storeBit(hasNext);
for (const value of chunks[index]) {
builder2.storeUint(value, 256);
}
if (hasNext) {
builder2.storeRef(buildChunk(index + 1));
}
return builder2.endCell();
};
const builder = beginCell2().storeUint(values.length, 8);
if (chunks.length === 0) {
builder.storeBit(false);
} else {
builder.storeBit(true).storeRef(buildChunk(0));
}
return builder.endCell();
}
function storePair(first, second) {
return beginCell2().storeSlice(first.beginParse()).storeSlice(second.beginParse()).endCell();
}
function plonkTolkMessageFromCalldata(args) {
const a = requireTupleCell(args, 0);
const b = requireTupleCell(args, 1);
const c = requireTupleCell(args, 2);
const z = requireTupleCell(args, 3);
const t1 = requireTupleCell(args, 4);
const t2 = requireTupleCell(args, 5);
const t3 = requireTupleCell(args, 6);
const wxi = requireTupleCell(args, 13);
const wxiw = requireTupleCell(args, 14);
const publicInputs = uint256ArrayToCell(requirePublicInputTuple(args));
const aUc = requireTupleCell(args, 16);
const bUc = requireTupleCell(args, 17);
const cUc = requireTupleCell(args, 18);
const zUc = requireTupleCell(args, 19);
const t1Uc = requireTupleCell(args, 20);
const t2Uc = requireTupleCell(args, 21);
const t3Uc = requireTupleCell(args, 22);
const wxiUc = requireTupleCell(args, 23);
const wxiwUc = requireTupleCell(args, 24);
const compressedTail = beginCell2().storeRef(storePair(t3, wxi)).storeRef(wxiw).endCell();
const compressed = beginCell2().storeRef(storePair(a, b)).storeRef(storePair(c, z)).storeRef(storePair(t1, t2)).storeRef(compressedTail).endCell();
const evaluations = beginCell2().storeRef(
beginCell2().storeUint(requireTupleInt(args, 7), 256).storeUint(requireTupleInt(args, 8), 256).storeUint(requireTupleInt(args, 9), 256)
).storeRef(
beginCell2().storeUint(requireTupleInt(args, 10), 256).storeUint(requireTupleInt(args, 11), 256).storeUint(requireTupleInt(args, 12), 256)
).endCell();
const uncompressedTail2 = beginCell2().storeRef(t3Uc).storeRef(wxiUc).storeRef(wxiwUc).endCell();
const uncompressedTail = beginCell2().storeRef(zUc).storeRef(t1Uc).storeRef(t2Uc).storeRef(uncompressedTail2).endCell();
const uncompressed = beginCell2().storeRef(aUc).storeRef(bUc).storeRef(cUc).storeRef(uncomp