Merge pull request #6 from voltrevo/chore/eslint-prettier

chore: add eslint and prettier
This commit is contained in:
Andrew Morris
2025-02-12 09:48:47 +11:00
committed by GitHub
25 changed files with 1590 additions and 149 deletions

38
.eslintrc.cjs Normal file
View File

@@ -0,0 +1,38 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:prettier/recommended',
'plugin:@typescript-eslint/recommended',
'xo/browser',
],
ignorePatterns: ['dist'],
parser: '@typescript-eslint/parser',
rules: {
indent: ['error', 2, { SwitchCase: 1 }],
'object-curly-spacing': ['error', 'always'],
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{
varsIgnorePattern: '^_',
argsIgnorePattern: '^_',
destructuredArrayIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
quotes: ['error', 'single', { avoidEscape: true }],
'comma-dangle': 'off',
'object-curly-newline': 'off',
'no-undef': 'off',
'no-mixed-operators': 'off',
'operator-linebreak': 'off',
'no-bitwise': 'off',
'no-constant-condition': 'off',
'no-useless-constructor': 'off',
'capitalized-comments': 'off',
'no-await-in-loop': 'off',
'no-return-await': 'off',
},
};

View File

@@ -1,15 +1,14 @@
## What is this PR doing?
## How can these changes be manually tested?
## Does this PR resolve or contribute to any issues?
## Checklist
- [ ] I have manually tested these changes
- [ ] Post a link to the PR in the group chat
## Guidelines
- If your PR is not ready, mark it as a draft

17
.prettierignore Normal file
View File

@@ -0,0 +1,17 @@
# dependencies
node_modules
package-lock.json
# production
dist
build
wasm/target
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

6
.prettierrc.json Normal file
View File

@@ -0,0 +1,6 @@
{
"arrowParens": "avoid",
"tabWidth": 2,
"singleQuote": true,
"printWidth": 80
}

22
.vscode/launch.json vendored
View File

@@ -8,31 +8,31 @@
"name": "tsx",
"type": "node",
"request": "launch",
// Debug current file in VSCode
"program": "${file}",
/*
Path to tsx binary
Assuming locally installed
*/
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/tsx",
/*
Open terminal when debugging starts (Optional)
Useful to see console.logs
*/
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
// Files to exclude from debugger (e.g. call stack)
"skipFiles": [
// Node.js internal core modules
"<node_internals>/**",
// Ignore all dependencies (optional)
"${workspaceFolder}/node_modules/**",
],
// Node.js internal core modules
"<node_internals>/**",
// Ignore all dependencies (optional)
"${workspaceFolder}/node_modules/**"
]
}
]
}
}

View File

@@ -1,4 +1,7 @@
{
"editor.rulers": [80],
"editor.tabSize": 2
}
"editor.tabSize": 2,
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
}
}

View File

@@ -7,13 +7,14 @@ your own.
## What is MPC?
MPC stands for *Multi-Party Computation*. In regular computation, all inputs,
MPC stands for _Multi-Party Computation_. In regular computation, all inputs,
outputs, and intermediate calculations are necessarily visible on the device
performing the computation. MPC, by contrast, allows multiple devices to
collaborate on a computation while keeping intermediate calculations and others'
inputs private.
Here's some ways that can be useful:
- Provide analysis on patient data to researchers without revealing the patient data
- Play [Rock Paper Scissors Lizard Spock](https://voltrevo.github.io/mpc-lizard-spock/) while keeping your move secret
- Hold an auction while keeping the bids secret (only the winning bidder and price is revealed)
@@ -33,6 +34,7 @@ For a more technical introduction, see [Computerphile's video on Garbled Circuit
## Usage Guide
In addition to `mpc-framework`, you will need:
- a circuit generator to turn your MPC program into a circuit (or byo precompiled or handwritten circuit)
- an mpc-framework backend to do the underlying cryptography
@@ -165,17 +167,17 @@ For clarity, a complete version of the example above is provided as
## **Circuit Generators**
| Name | Similar to | Related Repos |
| -------- | ---------- | ------------- |
| [`summon-ts`](https://github.com/voltrevo/summon-ts/) | TypeScript | [`summon`](https://github.com/voltrevo/summon/), [`boolify`](https://github.com/voltrevo/boolify/), [`ValueScript`](https://github.com/voltrevo/ValueScript/) |
| [`circom-2-arithc-ts`](https://github.com/voltrevo/circom-2-arithc-ts/) | Circom | [`circom-2-arithc`](https://github.com/namnc/circom-2-arithc/), [`circom`](https://github.com/iden3/circom/) |
| Name | Similar to | Related Repos |
| ----------------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`summon-ts`](https://github.com/voltrevo/summon-ts/) | TypeScript | [`summon`](https://github.com/voltrevo/summon/), [`boolify`](https://github.com/voltrevo/boolify/), [`ValueScript`](https://github.com/voltrevo/ValueScript/) |
| [`circom-2-arithc-ts`](https://github.com/voltrevo/circom-2-arithc-ts/) | Circom | [`circom-2-arithc`](https://github.com/namnc/circom-2-arithc/), [`circom`](https://github.com/iden3/circom/) |
## **Backends**
| Name | Description | Related Repos |
| ---- | ----------- | ------------- |
| Name | Description | Related Repos |
| ------------------------------------------------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`emp-wasm-backend`](https://github.com/voltrevo/emp-wasm-backend/) | Secure MPC using authenticated garbling | [`emp-wasm`](https://github.com/voltrevo/emp-wasm), [`emp-ag2pc`](https://github.com/emp-toolkit/emp-ag2pc/), [`emp-agmpc`](https://github.com/emp-toolkit/emp-agmpc/) |
| [`mpz-ts`](https://github.com/voltrevo/mpz-ts) | Semi-honest 2PC | [`mpz`](https://github.com/privacy-scaling-explorations/mpz) |
| [`mpz-ts`](https://github.com/voltrevo/mpz-ts) | Semi-honest 2PC | [`mpz`](https://github.com/privacy-scaling-explorations/mpz) |
## Example Projects

1371
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,10 @@
"main": "dist/src/index.js",
"scripts": {
"build": "rm -rf dist && tsc",
"test": "mocha --import=tsx tests/**/*.test.ts"
"test": "mocha --import=tsx tests/**/*.test.ts",
"lint": "eslint . --ext ts --report-unused-disable-directives --max-warnings 0",
"format": "prettier -c .",
"format:fix": "prettier -w ."
},
"keywords": [
"MPC",
@@ -26,6 +29,15 @@
"@types/chai": "^4.3.17",
"@types/mocha": "^10.0.7",
"chai": "^5.1.1",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^10.0.1",
"eslint-config-xo": "^0.44.0",
"eslint-plugin-prettier": "^5.2.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"prettier": "^3.4.2",
"emp-wasm-backend": "^0.2.1",
"mocha": "^10.7.0",
"summon-ts": "^0.2.5",

View File

@@ -1,6 +1,11 @@
import PlaintextBackendHostSession from "./PlaintextBackendHostSession.js";
import PlaintextBackendClientSession from "./PlaintextBackendClientSession.js";
import { Backend, BackendSession, Circuit, MpcSettings } from "mpc-framework-common";
import PlaintextBackendHostSession from './PlaintextBackendHostSession.js';
import PlaintextBackendClientSession from './PlaintextBackendClientSession.js';
import {
Backend,
BackendSession,
Circuit,
MpcSettings,
} from 'mpc-framework-common';
export default class PlaintextBackend implements Backend {
run(
@@ -10,7 +15,7 @@ export default class PlaintextBackend implements Backend {
input: Record<string, unknown>,
send: (to: string, msg: Uint8Array) => void,
): BackendSession {
const hostName = mpcSettings[0].name ?? "0";
const hostName = mpcSettings[0].name ?? '0';
if (name === hostName) {
return new PlaintextBackendHostSession(

View File

@@ -1,7 +1,7 @@
import { pack, unpack } from "msgpackr";
import defer from "../helpers/defer.js";
import { z } from "zod";
import { BackendSession, Circuit, MpcSettings } from "mpc-framework-common";
import { pack, unpack } from 'msgpackr';
import defer from '../helpers/defer.js';
import { z } from 'zod';
import { BackendSession, Circuit, MpcSettings } from 'mpc-framework-common';
export default class PlaintextBackendClientSession implements BackendSession {
outputReceived = defer<Record<string, unknown>>();

View File

@@ -1,11 +1,11 @@
import { pack, unpack } from "msgpackr";
import { pack, unpack } from 'msgpackr';
import z from 'zod';
import delay from "../helpers/delay.js";
import defer from "../helpers/defer.js";
import evaluate, { u32Arithmetic } from "../helpers/evaluate.js";
import errorToString from "../helpers/errorToString.js";
import { BackendSession, Circuit, MpcSettings } from "mpc-framework-common";
import delay from '../helpers/delay.js';
import defer from '../helpers/defer.js';
import evaluate, { u32Arithmetic } from '../helpers/evaluate.js';
import errorToString from '../helpers/errorToString.js';
import { BackendSession, Circuit, MpcSettings } from 'mpc-framework-common';
export default class PlaintextBackendHostSession implements BackendSession {
outputPromise: Promise<Record<string, unknown>>;
@@ -28,6 +28,7 @@ export default class PlaintextBackendHostSession implements BackendSession {
let shouldPing = true;
(async () => {
// eslint-disable-next-line no-unmodified-loop-condition
while (shouldPing) {
for (let i = 1; i < this.mpcSettings.length; i++) {
const to = this.mpcSettings[i].name ?? i.toString();
@@ -50,7 +51,7 @@ export default class PlaintextBackendHostSession implements BackendSession {
let selfResult: Record<string, unknown> = {};
for (let i = 0; i < this.mpcSettings.length; i++) {
const {name = i.toString(), outputs} = this.mpcSettings[i];
const { name = i.toString(), outputs } = this.mpcSettings[i];
const result: Record<string, unknown> = {};
for (const outputName of outputs) {

View File

@@ -1,5 +1,5 @@
import { Backend, Circuit, MpcSettings } from "mpc-framework-common";
import Session from "./Session.js";
import { Backend, Circuit, MpcSettings } from 'mpc-framework-common';
import Session from './Session.js';
export default class Protocol {
constructor(

View File

@@ -1,4 +1,9 @@
import { Backend, BackendSession, Circuit, MpcSettings } from "mpc-framework-common";
import {
Backend,
BackendSession,
Circuit,
MpcSettings,
} from 'mpc-framework-common';
export default class Session {
backendSession: BackendSession;

View File

@@ -2,4 +2,4 @@ export default function assert(value: unknown): asserts value {
if (!value) {
throw new Error('Assertion failed');
}
}
}

View File

@@ -1,6 +1,6 @@
export default function defer<T>() {
let resolve: ((value: T) => void) | undefined;
let reject: ((reason?: any) => void) | undefined;
let reject: ((reason?: unknown) => void) | undefined;
const promise = new Promise<T>((resolve_, reject_) => {
resolve = resolve_;

View File

@@ -1,3 +1,5 @@
export default async function delay(ms: number) {
await new Promise(resolve => setTimeout(resolve, ms));
await new Promise(resolve => {
setTimeout(resolve, ms);
});
}

View File

@@ -1,4 +1,4 @@
import { z } from "zod";
import { z } from 'zod';
const HasMessage = z.object({
message: z.string(),

View File

@@ -1,4 +1,5 @@
import { Circuit } from "mpc-framework-common";
/* eslint-disable camelcase */
import { Circuit } from 'mpc-framework-common';
export default function evaluate<T>(
circuit: Circuit,
@@ -56,13 +57,12 @@ export default function evaluate<T>(
const parts = line.split(' ');
const [inputLen, outputLen] = parts.slice(0, 2).map(Number);
const inputs = parts.slice(2, 2 + inputLen).map(i => wires[Number(i)]);
const outputIndexes = parts.slice(
2 + inputLen,
2 + inputLen + outputLen,
).map(Number);
const outputIndexes = parts
.slice(2 + inputLen, 2 + inputLen + outputLen)
.map(Number);
const op = parts[parts.length - 1];
@@ -79,10 +79,9 @@ export default function evaluate<T>(
const res: Record<string, T> = {};
for (
const [name, wireIndex] of
Object.entries(circuit.info.output_name_to_wire_index)
) {
for (const [name, wireIndex] of Object.entries(
circuit.info.output_name_to_wire_index,
)) {
res[name] = wires[wireIndex];
}
@@ -102,41 +101,42 @@ export const u32Arithmetic = {
typeof source === 'boolean' ||
typeof source === 'string'
) {
return BigInt(source) % (2n ** 32n);
return BigInt(source) % 2n ** 32n;
}
throw new Error('Can\'t interpret source as u32');
throw new Error("Can't interpret source as u32");
},
// eslint-disable-next-line complexity
combine(op, [a, b]) {
switch (op) {
case "AUnaryAdd": {
case 'AUnaryAdd': {
return [a];
}
case "AUnarySub": {
case 'AUnarySub': {
a = -a;
if (a < 0n) {
a += 2n ** 32n
a += 2n ** 32n;
}
return [a];
}
case "ANot": {
case 'ANot': {
return [a ? 0n : 1n];
}
case "ABitNot": {
return [~a + (2n ** 32n)]
case 'ABitNot': {
return [~a + 2n ** 32n];
}
case "AAdd": {
return [(a + b) % (2n ** 32n)]
case 'AAdd': {
return [(a + b) % 2n ** 32n];
}
case "ASub": {
case 'ASub': {
let res = a - b;
if (res < 0) {
@@ -146,77 +146,77 @@ export const u32Arithmetic = {
return [res];
}
case "AMul": {
return [(a * b) % (2n ** 32n)];
case 'AMul': {
return [(a * b) % 2n ** 32n];
}
case "ADiv": {
case 'ADiv': {
return [a / b];
}
case "AMod": {
case 'AMod': {
return [a % b];
}
case "AExp": {
case 'AExp': {
// Can be done more efficiently
return [(a ** b) % (2n ** 32n)];
return [a ** b % 2n ** 32n];
}
case "AEq": {
case 'AEq': {
return [a === b ? 1n : 0n];
}
case "ANeq": {
return [a !== b ? 1n : 0n];
case 'ANeq': {
return [a === b ? 0n : 1n];
}
case "ABoolAnd": {
case 'ABoolAnd': {
return [a && b ? 1n : 0n];
}
case "ABoolOr": {
case 'ABoolOr': {
return [a || b ? 1n : 0n];
}
case "ALt": {
case 'ALt': {
return [a < b ? 1n : 0n];
}
case "ALEq": {
case 'ALEq': {
return [a <= b ? 1n : 0n];
}
case "AGt": {
case 'AGt': {
return [a > b ? 1n : 0n];
}
case "AGEq": {
case 'AGEq': {
return [a >= b ? 1n : 0n];
}
case "ABitAnd": {
case 'ABitAnd': {
return [a & b];
}
case "ABitOr": {
case 'ABitOr': {
return [a | b];
}
case "AXor": {
case 'AXor': {
return [a ^ b];
}
case "AShiftL": {
return [(a << b) % (2n ** 32n)];
case 'AShiftL': {
return [(a << b) % 2n ** 32n];
}
case "AShiftR": {
case 'AShiftR': {
return [a >> b];
}
default:
throw new Error(`Unrecognized op ${op}`)
throw new Error(`Unrecognized op ${op}`);
}
},
} satisfies Arithmetic<bigint>;

View File

@@ -1,9 +1,5 @@
export default function once<T>(f: () => T): () => T {
let result: (
| undefined
| { error: unknown }
| { value: T }
) = undefined;
let result: undefined | { error: unknown } | { value: T };
return () => {
if (!result) {
@@ -22,4 +18,4 @@ export default function once<T>(f: () => T): () => T {
return result.value;
};
}
}

View File

@@ -1,6 +1,6 @@
import * as summon from 'summon-ts';
import once from "../../src/helpers/once";
import once from '../../src/helpers/once';
const aPlusB = once(async () => {
await summon.init();
@@ -10,7 +10,7 @@ const aPlusB = once(async () => {
export default function c(a: number, b: number) {
return a + b;
}
`
`,
});
});

View File

@@ -20,7 +20,9 @@ export function makeLocalCommsPair(): [LocalComms, LocalComms] {
return [a, b];
}
export class LocalCommsBuf extends EventEmitter<{ data(data: Uint8Array): void }> {
export class LocalCommsBuf extends EventEmitter<{
data(data: Uint8Array): void;
}> {
buf = new Uint8Array(1024);
bufLen = 0;

View File

@@ -1,7 +1,5 @@
export default function blockTrim(text: string) {
const lines = text.split('\n').map(
line => line.trim() === '' ? '' : line,
);
const lines = text.split('\n').map(line => (line.trim() === '' ? '' : line));
while (lines[0].trim() === '') {
lines.shift();
@@ -29,7 +27,5 @@ export default function blockTrim(text: string) {
}
}
return lines.map(
line => line.slice(minIndent),
).join('\n');
return lines.map(line => line.slice(minIndent)).join('\n');
}

View File

@@ -1,9 +1,9 @@
import { expect } from "chai";
import Protocol from "../src/Protocol";
import aPlusB from "./circuits/aPlusB";
import PlaintextBackend from "../src/PlaintextBackend/PlaintextBackend";
import { EventEmitter } from "ee-typed";
import assert from "../src/helpers/assert";
import { expect } from 'chai';
import Protocol from '../src/Protocol';
import aPlusB from './circuits/aPlusB';
import PlaintextBackend from '../src/PlaintextBackend/PlaintextBackend';
import { EventEmitter } from 'ee-typed';
import assert from '../src/helpers/assert';
describe('plaintext', () => {
it('3 + 5', async () => {
@@ -30,36 +30,24 @@ describe('plaintext', () => {
}>();
const aliceOutputPromise = (async () => {
const session = protocol.join(
'alice',
{ a: 3 },
(to, msg) => {
assert(to === 'bob');
messageEvents.emit('aliceToBob', msg);
},
);
const session = protocol.join('alice', { a: 3 }, (to, msg) => {
assert(to === 'bob');
messageEvents.emit('aliceToBob', msg);
});
messageEvents.on(
'bobToAlice',
msg => session.handleMessage('bob', msg),
);
messageEvents.on('bobToAlice', msg => session.handleMessage('bob', msg));
return await session.output();
})();
const bobOutputPromise = (async () => {
const session = protocol.join(
'bob',
{ b: 5 },
(to, msg) => {
assert(to === 'alice');
messageEvents.emit('bobToAlice', msg);
},
);
const session = protocol.join('bob', { b: 5 }, (to, msg) => {
assert(to === 'alice');
messageEvents.emit('bobToAlice', msg);
});
messageEvents.on(
'aliceToBob',
msg => session.handleMessage('alice', msg),
messageEvents.on('aliceToBob', msg =>
session.handleMessage('alice', msg),
);
return await session.output();

View File

@@ -11,7 +11,7 @@
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
@@ -25,9 +25,9 @@
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "ESNext", /* Specify what module code is generated. */
"rootDir": "./", /* Specify the root folder within your source files. */
"moduleResolution": "Bundler", /* Specify how TypeScript looks up a file from a given module specifier. */
"module": "ESNext" /* Specify what module code is generated. */,
"rootDir": "./" /* Specify the root folder within your source files. */,
"moduleResolution": "Bundler" /* Specify how TypeScript looks up a file from a given module specifier. */,
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
@@ -49,13 +49,13 @@
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
"declarationMap": true, /* Create sourcemaps for d.ts files. */
"declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
"declarationMap": true /* Create sourcemaps for d.ts files. */,
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
"sourceMap": true /* Create source map files for emitted JavaScript files. */,
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./dist", /* Specify an output folder for all emitted files. */
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
@@ -74,15 +74,15 @@
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
"isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
"isolatedModules": true /* Ensure that each file can be safely transpiled without relying on other imports. */,
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
"allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
"allowSyntheticDefaultImports": true /* Allow 'import x from y' when a module doesn't have a default export. */,
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
@@ -104,6 +104,6 @@
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}