diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1d80513 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,22 @@ +name: CI +on: [push] + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: checkout repo + uses: actions/checkout@v3 + + - name: install node + uses: actions/setup-node@v3 + with: + cache: yarn + node-version: 18 + + - name: install node deps + run: yarn install --frozen-lockfile + + - name: test + run: yarn test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f06235c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..b9f3fe1 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +node_modules +artifacts +cache +coverage* +gasReporterOutput.json +typechain +typechain-types +LICENSE diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..996cd8a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,45 @@ +{ + "overrides": [ + { + "files": ["*.ts"], + "options": { + "parser": "typescript", + "tabWidth": 4, + "semi": false, + "singleQuote": true, + "printWidth": 100 + } + }, + { + "files": ["*.json"], + "options": { + "parser": "json", + "tabWidth": 2 + } + }, + { + "files": ["*.html"], + "options": { + "parser": "html", + "tabWidth": 4 + } + }, + { + "files": ["*.css"], + "options": { + "parser": "css", + "tabWidth": 4 + } + }, + { + "files": ["*.md"], + "options": { + "parser": "markdown", + "tabWidth": 4, + "semi": false, + "singleQuote": true, + "printWidth": 100 + } + } + ] + } diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..53a1247 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Kevin Charm + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..48c834b --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# broth16 + +![](./broth16.jpg) + +This is an exercise to learn how Groth16 works. + +## Useful reference material + +- J. Groth, [On the Size of Pairing-based Non-interactive Arguments](https://eprint.iacr.org/2016/260.pdf) +- V. Buterin, [Quadratic Arithmetic Programs: from Zero to Hero](https://medium.com/@VitalikButerin/quadratic-arithmetic-programs-from-zero-to-hero-f6d558cea649) +- N. Gailly, [playsnark](https://github.com/nikkolasg/playsnark) diff --git a/broth16.jpg b/broth16.jpg new file mode 100644 index 0000000..65693fd Binary files /dev/null and b/broth16.jpg differ diff --git a/package.json b/package.json new file mode 100644 index 0000000..26848f9 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "@kevincharm/broth16", + "version": "1.0.0", + "description": "Toy Groth16 implementation", + "engines": { + "node": ">=18" + }, + "packageManager": "yarn@1.22.22", + "scripts": { + "prepare": "yarn tsc", + "test": "NODE_OPTIONS=--experimental-global-webcrypto ./node_modules/.bin/mocha -r ts-node/register test/**/*.spec.ts" + }, + "author": "Kevin Charm ", + "license": "MIT", + "devDependencies": { + "@types/chai": "^4.3.16", + "@types/mocha": "^10.0.7", + "@types/node": "^18", + "chai": "^4", + "mocha": "^10.6.0", + "prettier": "^3.3.2", + "ts-node": "^10.9.2", + "typescript": "^5.5.3" + }, + "dependencies": { + "@kevincharm/blstoise": "^1.0.1" + } +} diff --git a/src/groth16.ts b/src/groth16.ts new file mode 100644 index 0000000..67563b8 --- /dev/null +++ b/src/groth16.ts @@ -0,0 +1,232 @@ +import { Fr, PointG1, PointG2, validatePairing } from '@kevincharm/blstoise' +import { randomFr } from '@kevincharm/blstoise/dist/src/utils' +import { addPolys, evalPoly } from './poly' +import { QAP, computeSolutionPoly, computeDivisorPoly, tPoly } from './qap' + +/** + * Combined phase 1 & 2 trusted setup outputs for Groth16 + */ +export interface TrustedSetup { + power: number + tauG1: PointG1[] + tauG2: PointG2[] + alphaG1: PointG1 + betaG1: PointG1 + betaG2: PointG2 + gammaG2: PointG2 + deltaG1: PointG1 + deltaG2: PointG2 + cTauPublicG1: PointG1[] + cTauPrivateG1: PointG1[] + tTauInvDeltaG1: PointG1[] +} + +/** + * Perform a circuit-specific trusted setup for Groth16 proofs. + * @param qap Polynomials representing the quadratic arithmetic programme + */ +export function setup({ l, u, v, w }: QAP): TrustedSetup { + const degree = u[0].length + const tau = randomFr() // toxic waste + const alpha = randomFr() // toxic waste + const beta = randomFr() // toxic waste + const gamma = randomFr() // toxic waste + const delta = randomFr() // toxic waste + + const G1 = PointG1.generator() + const G2 = PointG2.generator() + const tauG1 = Array.from({ length: degree }, (_, i) => G1.mul(tau.exp(BigInt(i)).value)) + const tauG2 = Array.from({ length: degree }, (_, i) => G2.mul(tau.exp(BigInt(i)).value)) + const alphaG1 = G1.mul(alpha.value) + const betaG1 = G1.mul(beta.value) + const betaG2 = G2.mul(beta.value) + + const gammaG2 = G2.mul(gamma.value) + const deltaG1 = G1.mul(delta.value) + const deltaG2 = G2.mul(delta.value) + + // foreach row (polynomial) in QAP.u \i -> beta*u_i(x) + const betaU = u.map((uPoly) => uPoly.map((x) => x.mul(beta))) + // foreach row (polynomial) in QAP.v \i -> alpha*v_i(x) + const alphaV = v.map((vPoly) => vPoly.map((x) => x.mul(alpha))) + // From Groth16, 3.2 NIZK arguments for quadratic arithmetic programs + // This is the combined sum term from C and AB (for both public and private inputs) + // evaluated at tau: + // C = \sum_{i=0}^{m} \beta u_i(\tau) + \alpha v_i(\tau) + w_i(\tau) + const cG1: PointG1[] = [] + for (let i = 0; i < w.length; i++) { + const c = G1.mul(evalPoly(addPolys(addPolys(betaU[i], alphaV[i]), w[i]), tau).value) + cG1.push(c) + } + + // Public inputs - scale sum term by 1/gamma + // (1/gamma) * [C]_1 + const invGamma = gamma.inv() + const cTauPublicG1 = cG1.slice(0, l + 1).map((p) => p.mul(invGamma.value)) + + // Private inputs - scale sum term by 1/delta + // (1/delta) * [C]_1 + const invDelta = delta.inv() + const cTauPrivateG1 = cG1.slice(l + 1).map((p) => p.mul(invDelta.value)) + + // We need this later to compute h(tau)t(tau) in the proving step. + // See: https://github.com/nikkolasg/playsnark/blob/c406bfdeefbc60345696bfad9105568e91f04c13/groth16.go#L180 + // [tau^i * t(tau) / delta]_1 + const tx = tPoly(degree) + const tTau = evalPoly(tx, tau) + const tTauInvDeltaG1 = tauG1.map((ptau) => ptau.mul(tTau.value).mul(invDelta.value)) + + return { + power: degree, + tauG1, + tauG2, + alphaG1, + betaG1, + betaG2, + gammaG2, + deltaG1, + deltaG2, + cTauPublicG1, + cTauPrivateG1, + tTauInvDeltaG1, + } +} + +/** + * Verification key for Groth16 proofs; + * which is a subset of the circuit-specific trusted setup + */ +export interface VerificationKey { + alphaG1: PointG1 + betaG2: PointG2 + gammaG2: PointG2 + deltaG2: PointG2 + cTauPublicG1: PointG1[] +} + +/** Jens Groth's succinct noninteractive argument of knowledge */ +export type Proof = [PointG1, PointG1, PointG2] + +/** + * Compute a succinct Groth16 proof for a given programme and a witness vector. + * @param setup Circuit-specific trusted setup + * @param qap Polynomials representing the quadratic arithmetic programme + * @param witness Witness vector (i.e. a solution to the programme) + * @param shouldCheckProof Whether to check that the proof verifies before returning + * @returns A succinct proof that the witness satisfies the QAP + */ +export function prove( + setup: TrustedSetup, + qap: QAP, + witness: Fr[], + shouldCheckProof?: boolean, +): { proof: Proof; vk: VerificationKey } { + const r = randomFr() // toxic waste + const s = randomFr() // toxic waste + + // We fix the first witness to 1 (used for constants) + const preparedWitness = [Fr.one(), ...witness] + // Compute polynomials a_i u_i(x) + const { au, av, ht } = computeSolutionPoly(preparedWitness, qap) + + // A = [\alpha + \sum_{i=0}^{m} a_i u_i(\tau) + r*delta]_1 + const sumA = au.map(({ value }, i) => setup.tauG1[i].mul(value)).reduce((acc, p) => acc.add(p)) + const A = setup.alphaG1.add(sumA).add(setup.deltaG1.mul(r.value)) + // B = [\beta + \sum_{i=0}^{m} a_i v_i(\tau) + s*delta]_1 + const sumBG1 = av + .map(({ value }, i) => setup.tauG1[i].mul(value)) + .reduce((acc, p) => acc.add(p)) + const BG1 = setup.betaG1.add(sumBG1).add(setup.deltaG1.mul(s.value)) + // B = [\beta + \sum_{i=0}^{m} a_i v_i(\tau) + s*delta]_2 + const sumBG2 = av + .map(({ value }, i) => setup.tauG2[i].mul(value)) + .reduce((acc, p) => acc.add(p)) + const BG2 = setup.betaG2.add(sumBG2).add(setup.deltaG2.mul(s.value)) + + // We need the +h(\tau)*t(\tau) part of C + const h = computeDivisorPoly(ht, tPoly(qap.u[0].length)) + const htTauG1 = h + .map((x, i) => setup.tTauInvDeltaG1[i].mul(x.value)) + .reduce((acc, p) => acc.add(p)) + // Private inputs (+ intermediate constraints) part of C + // \sum_{i=l+1}^{m} a_i(\beta u_i(\tau) + \alpha v_i(\tau) + w_i(\tau) + const sumC = preparedWitness + .slice(qap.l + 1) + .map(({ value }, i) => setup.cTauPrivateG1[i].mul(value)) + .reduce((acc, p) => acc.add(p)) + // -(r * s * \delta) + const negRsDelta = setup.deltaG1.mul(r.mul(s).value).neg() + // Full C term evaluated in G1 + // C = \frac{1}{\delta} (\sum_{i=l+1}^{m} a_i(\beta u_i(\tau) + \alpha v_i(\tau) + w_i(\tau)) + h(\tau)t(\tau)) + // + As + rB - rs\delta + const C = sumC.add(htTauG1).add(A.mul(s.value)).add(BG1.mul(r.value)).add(negRsDelta) + // Proof triple, as described by Groth, 3.2 NIZK arguments for quadratic arithmetic programs + const proof: [PointG1, PointG1, PointG2] = [A, C, BG2] + + // This is the public inputs part + // \frac{1}{\gamma} (\sum_{i=0}^{l} a_i(\beta u_i(\tau) + \alpha v_i(\tau) + w_i(\tau))) + const publicInputsSumG1 = setup.cTauPublicG1 + .map((p, i) => p.mul(preparedWitness[i].value)) + .reduce((acc, p) => acc.add(p)) + + if (shouldCheckProof) { + // Check that proof verifies + const ps = [A.neg(), setup.alphaG1, publicInputsSumG1, C] + const qs = [BG2, setup.betaG2, setup.gammaG2, setup.deltaG2] + if (!validatePairing(ps, qs)) { + throw new Error('Pairing check failed') + } + } + + return { + proof, + vk: { + alphaG1: setup.alphaG1, + betaG2: setup.betaG2, + gammaG2: setup.gammaG2, + deltaG2: setup.deltaG2, + cTauPublicG1: setup.cTauPublicG1, + }, + } +} + +/** + * Verify a Groth16 proof for a given public input vector. + * + * @param proof Groth16 proof + * @param publicInputs Public input vector + * @param vk Circuit-specific trusted setup + * @returns True if the proof is valid + */ +export function verify(proof: Proof, publicInputs: Fr[], vk: VerificationKey): boolean { + const [A, C, BG2] = proof + const [alphaG1, betaG2, gammaG2, deltaG2] = [vk.alphaG1, vk.betaG2, vk.gammaG2, vk.deltaG2] + + // We fix the first public input to 1 (used for constants) + const preparedPublicInputs = [Fr.one(), ...publicInputs] + if (preparedPublicInputs.length !== vk.cTauPublicG1.length) { + throw new Error( + `Invalid number of public inputs: expected ${vk.cTauPublicG1.length - 1}, got ${publicInputs.length}`, + ) + } + // \sum_{i=0}^{l} a_i * [\frac{\beta u_i(\tau) + \alpha v_i(\tau) + w_i(\tau)}{\gamma}]_1 + const publicInputsSumG1 = vk.cTauPublicG1 + .map((p, i) => p.mul(preparedPublicInputs[i].value)) + .reduce((acc, p) => acc.add(p)) + + // Now from the Groth16 paper, 3.2 NIZK arguments for quadratic arithmetic programs. + // + // Parse proof = ([A]_1, [C]_1, [B]_2) + // + // Accept the proof iff + // [A]_1 * [B]_2 + // = [\alpha]_1 * [\beta]_2 + // + (\sum_{i=0}^{l} a_i * [\frac{\beta u_i(\tau) + \alpha v_i(\tau) + w_i(\tau)}{\gamma}]_1) * [\gamma]_2 + // + [C]_1 * [\delta]_2 + // Turn this into a pairing check: + // Given asymmetric pairing e(P,Q) :: G1 x G2 -> GT + // e([-A]_1, [B]_2) * e([\alpha]_1, [\beta]_2) * e([publicInputsSum]_1, [\gamma]_2) * e([C]_1, [\delta]_2) = 1 + const ps = [A.neg(), alphaG1, publicInputsSumG1, C] + const qs = [BG2, betaG2, gammaG2, deltaG2] + return validatePairing(ps, qs) +} diff --git a/src/poly.ts b/src/poly.ts new file mode 100644 index 0000000..08d10e1 --- /dev/null +++ b/src/poly.ts @@ -0,0 +1,197 @@ +import { Fr } from '@kevincharm/blstoise' + +// Most of these functions come from Vitalik's QAP implementation: +// https://github.com/ethereum/research/blob/master/zksnark/qap_creator.py + +/** + * Helper to convert a list of coefficients to a list of polynomial + * curve scalars coefficients. + * + * @param coeffs Coefficients + * @returns Polynomial in F_r + */ +export function toPoly(coeffs: (number | bigint)[]): Fr[] { + return coeffs.map((c) => new Fr(BigInt(c))) +} + +/** + * Create a zero poly + * + * @param degree Degree of polynomial + * @returns Polynomial with all coefficients zero + */ +export function zeroPoly(degree: number): Fr[] { + return Array.from({ length: degree }, () => Fr.zero()) +} + +/** + * Add two polynomials + * + * @param lhs Addend + * @param rhs Addend + * @returns Sum + */ +export function addPolys(lhs: Fr[], rhs: Fr[]): Fr[] { + const length = Math.max(lhs.length, rhs.length) + const result: Fr[] = Array.from({ length }, () => Fr.zero()) + for (let i = 0; i < lhs.length; i++) { + result[i] = result[i].add(lhs[i]) + } + for (let i = 0; i < rhs.length; i++) { + result[i] = result[i].add(rhs[i]) + } + return result +} + +/** + * Subtract two polynomials + * + * @param lhs Minuend + * @param rhs Subtrahend + * @returns Difference + */ +export function subPolys(lhs: Fr[], rhs: Fr[]): Fr[] { + const length = Math.max(lhs.length, rhs.length) + const result: Fr[] = Array.from({ length }, (_, i) => (i < lhs.length ? lhs[i] : Fr.zero())) + for (let i = 0; i < rhs.length; i++) { + result[i] = result[i].sub(rhs[i]) + } + return result +} + +/** + * Polynomial multiplication + * + * @param lhs Multiplicand + * @param rhs Multiplier + * @returns Multiplication + */ +export function mulPolys(lhs: Fr[], rhs: Fr[]): Fr[] { + const length = lhs.length + rhs.length - 1 + const result: Fr[] = Array.from({ length }, () => Fr.zero()) + for (let i = 0; i < lhs.length; i++) { + for (let j = 0; j < rhs.length; j++) { + result[i + j] = result[i + j].add(lhs[i].mul(rhs[j])) + } + } + return result +} + +/** + * Polynomial division + * + * @param lhs Dividend + * @param rhs Divisor + * @returns Quotient and remainder + */ +export function divPolys(lhs: Fr[], rhs: Fr[]): [quotient: Fr[], remainder: Fr[]] { + const length = lhs.length - rhs.length + 1 + const result = Array.from({ length }, () => Fr.zero()) + let remainder = lhs // ref + while (remainder.length >= rhs.length) { + const leadingFactor = remainder.at(-1)!.mul(rhs.at(-1)!.inv()) + const pos = remainder.length - rhs.length + result[pos] = leadingFactor + const spent = [...zeroPoly(pos), leadingFactor] + remainder = subPolys(remainder, mulPolys(rhs, spent)).slice(0, -1) + } + return [result, remainder] +} + +/** + * Evaluate a polynomial p(x) + * + * @param p Polynomial + * @param x Position + * @returns p(x) + */ +export function evalPoly(p: Fr[], x: Fr): Fr { + return p.reduce((acc, coeff, i) => acc.add(coeff.mul(x.exp(BigInt(i)))), Fr.zero()) +} + +/** + * Compute a polynomial p(x) s.t. p(x) = 0 foreach x in xs + * Borrowed from https://github.com/GuildOfWeavers/galois/blob/master/lib/PrimeField.ts#L992 + * + * @param xs + * @returns + */ +function zpoly(xs: Fr[]): Fr[] { + const result: Fr[] = Array.from({ length: xs.length + 1 }, () => Fr.zero()) + result[result.length - 1] = Fr.one() + + for (let i = 0, p = result.length - 2; i < xs.length; i++, p--) { + result[p] = Fr.zero() + for (let j = p; j < result.length - 1; j++) { + result[j] = result[j].sub(result[j + 1].mul(xs[i])) + } + } + return result +} + +/** + * Lagrange interpolation + * Borrowed from https://github.com/GuildOfWeavers/galois/blob/f3d9cfbf2fe7857f3840bdba3406e2ba9ea548c7/lib/PrimeField.ts#L808 + * + * @param xs + * @param ys + * @returns Interpolated polynomial fitting f(xs) = ys + */ +export function interpolate(xs: Fr[], ys: Fr[]): Fr[] { + if (xs.length !== ys.length) { + throw new Error('length mismatch') + } + + const root = zpoly(xs) + const divisor = [Fr.zero(), Fr.one()] + const numerators: Fr[][] = Array.from({ length: xs.length }, () => []) + for (let i = 0; i < xs.length; i++) { + divisor[0] = xs[i].neg() + ;[numerators[i]] = divPolys(root, divisor) + } + + const denominators: Fr[] = Array.from({ length: xs.length }, () => Fr.zero()) + for (let i = 0; i < xs.length; i++) { + denominators[i] = evalPoly(numerators[i], xs[i]) + } + const invDenValues = denominators.map((d) => d.inv()) + + const r = Array.from({ length: xs.length }, () => Fr.zero()) + for (let i = 0; i < xs.length; i++) { + const ySlice = ys[i].mul(invDenValues[i]) + for (let j = 0; j < xs.length; j++) { + if (!numerators[i][j].equals(Fr.zero()) && !ys[i].equals(Fr.zero())) { + r[j] = r[j].add(numerators[i][j].mul(ySlice)) + } + } + } + return r +} + +/** + * Transpose a matrix + * Matrix must be correctly formed! + * + * e.g. + * [[1, 2, 3], + * [4, 5, 6]] + * => + * [[1, 4], + * [2, 5], + * [3, 6]] + * + * @param matrix + * @returns Transposed matrix + */ +export function transpose(matrix: T[][]) { + // Assumes an m x n matrix is correctly formed + const m = matrix.length // rows + const n = matrix[0].length // cols + const out: T[][] = Array.from({ length: n }, () => Array.from({ length: m }, () => null!)) + for (let i = 0; i < n; i++) { + for (let j = 0; j < m; j++) { + out[i][j] = matrix[j][i] + } + } + return out +} diff --git a/src/qap.ts b/src/qap.ts new file mode 100644 index 0000000..1cef903 --- /dev/null +++ b/src/qap.ts @@ -0,0 +1,107 @@ +import { Fr } from '@kevincharm/blstoise' +import { transpose, interpolate, addPolys, mulPolys, subPolys, evalPoly, divPolys } from './poly' + +export type FrMatrix = Fr[][] + +/** + * Z is minimal polynomial t(x) = (x - 1)(x - 2)...(x - n) + * s.t. it is equal to zero at all points that correspond to our gates. + * Any polynomial that is equal to zero at all of these points must be + * a multiple of Z. + * If a polynomial is a multiple of Z, then its evaluation at any of those + * points will be zero. We will exploit this equivalence. + * Generate t(x) = (x - 1)(x - 2)...(x - n) + * + * @param degree The degree of the polynomial + */ +export function tPoly(degree: number): Fr[] { + let t = [Fr.one()] + for (let i = 1n; i <= degree; i++) { + t = mulPolys(t, [new Fr(i).neg(), Fr.one()]) + } + return t +} + +/** + * Quadratic arithmetic programme + */ +export interface QAP { + /** Number of public inputs in the witness vector */ + l: number + /** Lagrange basis for R1CS "A" gates */ + u: FrMatrix + /** Lagrange basis for R1CS "B" gates */ + v: FrMatrix + /** Lagrange basis for R1CS "C" gates */ + w: FrMatrix +} + +/** + * Create a quadratic arithmetic programme from an R1CS matrix triple (A, B, C) + */ +export function fromR1cstoQap(l: number, A: FrMatrix, B: FrMatrix, C: FrMatrix): QAP { + const A_t = transpose(A) + const B_t = transpose(B) + const C_t = transpose(C) + + // These are the x values or "positions" where each element of the transposed + // matrices are evaluated at. + const xs = Array.from({ length: A_t[0].length }, (_, i) => new Fr(BigInt(i + 1))) + + // Each element is a polynomial evaluation at its respective index. + const Upolys = A_t.map((row) => interpolate(xs, row)) + const Vpolys = B_t.map((row) => interpolate(xs, row)) + const Wpolys = C_t.map((row) => interpolate(xs, row)) + + return { + l, + u: Upolys, + v: Vpolys, + w: Wpolys, + } +} + +/** + * Compute solution polynomials given a witness vector and a QAP. + * + * @param witness witness vector + * @param qap Quadratic arithmetic programme + * @returns Solution polynomials + */ +export function computeSolutionPoly(witness: Fr[], { u, v, w }: QAP) { + const au = u.reduce((acc, row, i) => addPolys(acc, mulPolys([witness[i]], row)), []) + const av = v.reduce((acc, row, i) => addPolys(acc, mulPolys([witness[i]], row)), []) + const aw = w.reduce((acc, row, i) => addPolys(acc, mulPolys([witness[i]], row)), []) + // h(x)*t(x) + const ht = subPolys(mulPolys(au, av), aw) + + for (let i = 1n; i <= u[0].length; i++) { + if (!evalPoly(ht, new Fr(i)).equals(Fr.zero())) { + throw new Error(`Solution polynomial does not evaluate to zero at ${i}`) + } + } + + return { + au, + av, + ht, + } +} + +/** + * Divide the "solution polynomial" by the minimal polynomial t(x), and verify + * that the remainder is zero. + * The output here is h(x). + * + * @param ht h(x)t(x) from the solution polynomial + * @param t minimal polynomial t(x) + */ +export function computeDivisorPoly(ht: Fr[], t: Fr[]): Fr[] { + const [h, remainder] = divPolys(ht, t) + for (const x in remainder) { + if (!remainder[x].equals(Fr.zero())) { + throw new Error(`Remainder is not zero at ${x}`) + } + } + return h +} diff --git a/test/groth16.spec.ts b/test/groth16.spec.ts new file mode 100644 index 0000000..b57cca3 --- /dev/null +++ b/test/groth16.spec.ts @@ -0,0 +1,86 @@ +import { expect } from 'chai' +import { setup, prove, verify } from '../src/groth16' +import { FrMatrix, fromR1cstoQap } from '../src/qap' +import { Fr } from '@kevincharm/blstoise' + +function prepareMatrix(matrix: number[][]): FrMatrix { + return matrix.map((row) => row.map((x) => new Fr(BigInt(x)))) +} + +// Vitalik's famous example from https://medium.com/@VitalikButerin/quadratic-arithmetic-programs-from-zero-to-hero-f6d558cea649 +// We want to prove that we know the solution to a cubic equation: +// x^3 + x + 5 = 35 (where the answer x = 3). +// We define the equation as follows: +// y = x**3 +// output = y + x + 5 +// +// We flatten the equation to quadratic ones: +// t1 = x * x +// y = t1 * x +// t2 = y + x +// output = t2 + 5 +// +// Note that t1 and t2 are *intermediate variables* that were created in order to decompose the original +// equation into a sequence of (at most) quadratic equations. +// +// Now we convert the gates (i.e. the multiplications and additions) into R1CS. +// In R1CS, we have groups of 3 vectors (a,b,c), and the solution to an R1CS programme is a witness s vector +// that satisfies the equation s.a * s.b = s.c (. = dot product) +// +// Mapping: +// [1, x, output, t1, y, t2] +// The solution vector will consist of assignments for all of the above variables in that order. +// +// (a,b,c) for the 1st gate (t1 = x * x): +// a = [0, 1, 0, 0, 0, 0] +// b = [0, 1, 0, 0, 0, 0] +// c = [0, 0, 0, 1, 0, 0] +// +// (a,b,c) for the 2nd gate (y = t1 * x): +// a = [0, 0, 0, 1, 0, 0] +// b = [0, 1, 0, 0, 0, 0] +// c = [0, 0, 0, 0, 1, 0] +// +// (a,b,c) for the 3rd gate (t2 = y + x): +// a = [0, 1, 0, 0, 1, 0] <-- "y + x" +// b = [1, 0, 0, 0, 0, 0] <-- "addition check" of s.a +// c = [0, 0, 0, 0, 0, 1] +// +// (a,b,c) for the 4th gate (output = t2 + 5): +// a = [5, 0, 0, 0, 0, 1] +// b = [1, 0, 0, 0, 0, 0] +// c = [0, 0, 1, 0, 0, 0] + +const R1CS = { + A: prepareMatrix([ + [0, 1, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0], + [0, 1, 0, 0, 1, 0], + [5, 0, 0, 0, 0, 1], + ]), + B: prepareMatrix([ + [0, 1, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0], + ]), + C: prepareMatrix([ + [0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 1], + [0, 0, 1, 0, 0, 0], + ]), +} +const witness = [3, 35, 9, 27, 30].map((x) => new Fr(BigInt(x))) + +describe('groth16', () => { + it('compute and verify proof', () => { + const l = 1 + const qap = fromR1cstoQap(l, R1CS.A, R1CS.B, R1CS.C) + const trustedSetup = setup(qap) + const { proof, vk } = prove(trustedSetup, qap, witness) + + const publicInputs = witness.slice(0, l) + expect(verify(proof, publicInputs, vk)).to.equal(true) + }).timeout(10000) +}) diff --git a/test/poly.spec.ts b/test/poly.spec.ts new file mode 100644 index 0000000..92d1121 --- /dev/null +++ b/test/poly.spec.ts @@ -0,0 +1,63 @@ +import { expect } from 'chai' +import { addPolys, divPolys, interpolate, mulPolys, subPolys, toPoly, transpose } from '../src/poly' +import { Fr } from '@kevincharm/blstoise' + +function normalise(poly: (Fr | number | bigint)[]): string[] { + return poly.map((x) => { + if (x instanceof Fr) { + return String(x.value) + } else { + return String(x) + } + }) +} + +describe('poly', () => { + it('add', () => { + const lhs = toPoly([1, 2, 3]) + const rhs = toPoly([3, 2, 1]) + expect(normalise(addPolys(lhs, rhs))).to.deep.eq(normalise([4, 4, 4])) + }) + + it('sub', () => { + const lhs = toPoly([0, 1, 2]) + const rhs = toPoly([1, 1, 1]) + expect(normalise(subPolys(lhs, rhs))).to.deep.eq( + normalise([Fr.zero().sub(new Fr(1n)).value, 0, 1]), + ) + }) + + it('mul', () => { + const lhs = toPoly([1, 2, 3]) + const rhs = toPoly([7, 8, 9]) + expect(normalise(mulPolys(lhs, rhs))).to.deep.eq( + normalise([1 * 7, 1 * 8 + 2 * 7, 1 * 9 + 2 * 8 + 3 * 7, 2 * 9 + 3 * 8, 3 * 9]), + ) + }) + + it('div', () => { + const lhs = toPoly([1, 4]) + const rhs = toPoly([1, 2]) + const [quotient, remainder] = divPolys(lhs, rhs) + expect(normalise(quotient)).to.deep.eq(normalise([2])) + expect(normalise(remainder)).to.deep.eq(normalise([Fr.one().sub(new Fr(2n))])) + }) + + it('lagrange interpolation', () => { + const l = interpolate(toPoly([1, 2, 3]), toPoly([4, 5, 6])) + expect(normalise(l)).to.deep.eq(normalise([3, 1, 0])) + }) + + it('matrix transpose', () => { + const transposed = transpose([ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + ]) + expect(transposed).to.deep.eq([ + [1, 4, 7], + [2, 5, 8], + [3, 6, 9], + ]) + }) +}) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d4882b8 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es2020", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "outDir": "dist", + "declaration": true + }, + "include": ["./src", "./test"] +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..f33d913 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,693 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@kevincharm/blstoise@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@kevincharm/blstoise/-/blstoise-1.0.1.tgz#751e2233024940d601c2a93823dd990e1fd080aa" + integrity sha512-MXPrvGQPbLsvtbQv2hIVE8/jmPwMwxxWb6R3gwclxx7q1eLRPlZfzXotsUhbvn4LUGoR6PsqUhCOm7dTcp6nvA== + dependencies: + "@kevincharm/sharmander256" "^1.0.0" + +"@kevincharm/sharmander256@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@kevincharm/sharmander256/-/sharmander256-1.0.0.tgz#252020f0b0e6cb12a2ebbb40de7313a351ebc3ef" + integrity sha512-TGR1boI9ppvj99faQYwUYUeW7N2rJCW+uu5NcFPtbLJQWeWy/bojLr46flj+s3zs+uaeF6KLfetWKR9FvPDrfA== + +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/chai@^4.3.16": + version "4.3.16" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.16.tgz#b1572967f0b8b60bf3f87fe1d854a5604ea70c82" + integrity sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ== + +"@types/mocha@^10.0.7": + version "10.0.7" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.7.tgz#4c620090f28ca7f905a94b706f74dc5b57b44f2f" + integrity sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw== + +"@types/node@^18": + version "18.19.39" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.39.tgz#c316340a5b4adca3aee9dcbf05de385978590593" + integrity sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ== + dependencies: + undici-types "~5.26.4" + +acorn-walk@^8.1.1: + version "8.3.3" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e" + integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw== + dependencies: + acorn "^8.11.0" + +acorn@^8.11.0, acorn@^8.4.1: + version "8.12.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== + +ansi-colors@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browser-stdout@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +chai@^4: + version "4.4.1" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1" + integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" + pathval "^1.1.1" + type-detect "^4.0.8" + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +check-error@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" + integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== + dependencies: + get-func-name "^2.0.2" + +chokidar@^3.5.3: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +debug@^4.3.5: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +deep-eql@^4.1.3: + version "4.1.4" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.4.tgz#d0d3912865911bb8fac5afb4e3acfa6a28dc72b7" + integrity sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg== + dependencies: + type-detect "^4.0.0" + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +diff@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" + integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +escalade@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-func-name@^2.0.1, get-func-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +loupe@^2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" + integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== + dependencies: + get-func-name "^2.0.1" + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +minimatch@^5.0.1, minimatch@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +mocha@^10.6.0: + version "10.6.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.6.0.tgz#465fc66c52613088e10018989a3b98d5e11954b9" + integrity sha512-hxjt4+EEB0SA0ZDygSS015t65lJw/I2yRCS3Ae+SJ5FrbzrXgfYwJr96f0OvIXdj7h4lv/vLCrH3rkiuizFSvw== + dependencies: + ansi-colors "^4.1.3" + browser-stdout "^1.3.1" + chokidar "^3.5.3" + debug "^4.3.5" + diff "^5.2.0" + escape-string-regexp "^4.0.0" + find-up "^5.0.0" + glob "^8.1.0" + he "^1.2.0" + js-yaml "^4.1.0" + log-symbols "^4.1.0" + minimatch "^5.1.6" + ms "^2.1.3" + serialize-javascript "^6.0.2" + strip-json-comments "^3.1.1" + supports-color "^8.1.1" + workerpool "^6.5.1" + yargs "^16.2.0" + yargs-parser "^20.2.9" + yargs-unparser "^2.0.0" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +prettier@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.2.tgz#03ff86dc7c835f2d2559ee76876a3914cec4a90a" + integrity sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +ts-node@^10.9.2: + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +type-detect@^4.0.0, type-detect@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +typescript@^5.5.3: + version "5.5.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.3.tgz#e1b0a3c394190838a0b168e771b0ad56a0af0faa" + integrity sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +workerpool@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" + integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@^20.2.2, yargs-parser@^20.2.9: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-unparser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==