Skip to content

Commit

Permalink
ZK-559: Add shielder-sdk-crypto package
Browse files Browse the repository at this point in the history
  • Loading branch information
kroist committed Jan 6, 2025
1 parent 56627ce commit 22d941a
Show file tree
Hide file tree
Showing 10 changed files with 1,685 additions and 0 deletions.
1 change: 1 addition & 0 deletions ts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist/
1,379 changes: 1,379 additions & 0 deletions ts/pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions ts/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
packages:
- "shielder-sdk-crypto"
3 changes: 3 additions & 0 deletions ts/shielder-sdk-crypto/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"trailingComma": "none"
}
40 changes: 40 additions & 0 deletions ts/shielder-sdk-crypto/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import globals from "globals";
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";

export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
eslintPluginPrettierRecommended,
{
languageOptions: {
globals: {
...globals.jest,
...globals.node,
},
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
},
{
ignores: [
"dist/",
"src/_generated/",
"eslint.config.mjs",
"update-imports.mjs",
],
},
{
rules: {
"@typescript-eslint/unbound-method": [
"error",
{
ignoreStatic: true,
},
],
},
},
);
29 changes: 29 additions & 0 deletions ts/shielder-sdk-crypto/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "shielder-sdk-crypto",
"version": "0.0.1",
"description": "",
"scripts": {
"build": "tsc --project tsconfig.json && tsc-alias -p tsconfig.json",
"lint": "eslint . --report-unused-disable-directives --max-warnings 0"
},
"keywords": [],
"author": "",
"license": "ISC",
"files": [
"/dist"
],
"main": "dist/index.js",
"devDependencies": {
"@eslint/js": "^9.15.0",
"@types/node": "^22.10.0",
"eslint": "^9.15.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"globals": "^15.12.0",
"prettier": "^3.4.1",
"tsc-alias": "^1.8.10",
"typescript": "^5.7.2",
"typescript-eslint": "^8.16.0",
"viem": "^2.21.51"
}
}
40 changes: 40 additions & 0 deletions ts/shielder-sdk-crypto/src/cryptoClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {
DepositPubInputs,
DepositValues,
NewAccountPubInputs,
NewAccountValues,
Proof,
Scalar,
ShielderActionSecrets,
WithdrawPubInputs,
WithdrawValues
} from "./index";

export interface CryptoClient {
proveNewAccount(values: NewAccountValues): Promise<Proof>;

verifyNewAccount(
proof: Proof,
pubInputs: NewAccountPubInputs
): Promise<boolean>;

proveDeposit(values: DepositValues): Promise<Proof>;

verifyDeposit(proof: Proof, pubInputs: DepositPubInputs): Promise<boolean>;

proveWithdraw(values: WithdrawValues): Promise<Proof>;

verifyWithdraw(proof: Proof, pubInputs: WithdrawPubInputs): Promise<boolean>;

proveAndVerifyMerkle(): Promise<Proof>;

poseidonHash(inputs: Scalar[]): Promise<Scalar>;

getSecrets(id: Scalar, nonce: number): Promise<ShielderActionSecrets>;

privateKeyToScalar(hex: `0x${string}`): Promise<Scalar>;

treeHeight(): Promise<number>;

arity(): Promise<number>;
}
66 changes: 66 additions & 0 deletions ts/shielder-sdk-crypto/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Scalar, scalarToBigint, scalarsEqual, isBigintScalar } from "./scalar";
import { CryptoClient } from "@/cryptoClient";

export type Proof = Uint8Array;

export interface NewAccountPubInputs {
hNote: Scalar;
hId: Scalar;
initialDeposit: Scalar;
}

export interface NewAccountValues {
id: Scalar;
nullifier: Scalar;
trapdoor: Scalar;
initialDeposit: Scalar;
}

export interface DepositPubInputs {
idHiding: Scalar;
merkleRoot: Scalar;
hNullifierOld: Scalar;
hNoteNew: Scalar;
value: Scalar;
}

export interface DepositValues {
id: Scalar;
nonce: Scalar;
nullifierOld: Scalar;
trapdoorOld: Scalar;
accountBalanceOld: Scalar;
path: Uint8Array;
value: Scalar;
nullifierNew: Scalar;
trapdoorNew: Scalar;
}

export interface WithdrawPubInputs {
idHiding: Scalar;
merkleRoot: Scalar;
hNullifierOld: Scalar;
hNoteNew: Scalar;
value: Scalar;
commitment: Scalar;
}

export interface WithdrawValues {
id: Scalar;
nonce: Scalar;
nullifierOld: Scalar;
trapdoorOld: Scalar;
accountBalanceOld: Scalar;
path: Uint8Array;
value: Scalar;
nullifierNew: Scalar;
trapdoorNew: Scalar;
commitment: Scalar;
}

export type ShielderActionSecrets = {
nullifier: Scalar;
trapdoor: Scalar;
};

export { Scalar, scalarToBigint, isBigintScalar, scalarsEqual, CryptoClient };
102 changes: 102 additions & 0 deletions ts/shielder-sdk-crypto/src/scalar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* @typedef {Uint8Array} Scalar A scalar type, which wraps
* raw byte representation of halo2curves::bn256::Fr.
* It also exposes a method to convert unsigned BigInt to scalar.
*/

import { Address, hexToBigInt } from "viem";

export const r =
21888242871839275222246405745257275088548364400416034343698204186575808495617n;

function bytesToBigint(bytes: Uint8Array): bigint {
let result = 0n;
for (let i = 31; i >= 0; i--) {
result <<= 8n;
result += BigInt(bytes[i]);
}
return result;
}

export class Scalar {
bytes: Uint8Array;

/**
* @param bytes the bytes of the scalar in little-endian form
* @throws if the bytes are not 32 bytes long
* use only when wrapping WASM-produced scalar output
*/
constructor(bytes: Uint8Array) {
if (bytes.length !== 32) {
throw new Error(`Scalar must be 32 bytes long, but got ${bytes.length}`);
}
const bigint = bytesToBigint(bytes);
if (bigint < 0 || bigint >= r) {
throw new Error(`Scalar must be between 0 and ${r - 1n}`);
}
this.bytes = bytes;
}

/**
* Converts a bigint to a scalar.
* A value is convertible to scalar type if it is a non-negative integer less than r.
*
* @param value the value to convert
* @returns the value as a scalar
* @throws if the value is not convertible to scalar
*/
static fromBigint(value: bigint) {
return new Scalar(bigintToScalarBytes(value));
}

/**
* Converts an ethereum address to a scalar.
* @param address ethereum address
* @returns scalar representation of the address
* @throws if the address is incorrect (not 20 bytes hex string)
*/
static fromAddress(address: Address) {
const addressBigint = hexToBigInt(address);
return Scalar.fromBigint(addressBigint);
}
}

/**
* Checks if a value is convertible to scalar type.
* A value is convertible to scalar type if it is a non-negative integer less than r.
*
* @param value the value to check
* @returns if the value is convertible to scalar type
*/
export function isBigintScalar(value: bigint): boolean {
return value >= 0n && value < r;
}

/**
* Converts a bigint to a raw byte representation in
* halo2curves::bn256::Fr little-endian format.
* A value is convertible if it is a non-negative integer less than r.
*
* @param value the value to convert
* @returns the value as a raw bytes
* @throws if the value is not convertible to scalar type
*/
function bigintToScalarBytes(value: bigint): Uint8Array {
if (!isBigintScalar(value)) {
throw new Error(`Value ${value} is not a scalar`);
}
const result = new Uint8Array(32);
for (let i = 0; i < 32; i++) {
result[i] = Number(value & 0xffn);
value >>= 8n;
}
return result;
}

export function scalarToBigint(scalar: Scalar): bigint {
return bytesToBigint(scalar.bytes);
}

export function scalarsEqual(a: Scalar, b: Scalar): boolean {
return a.bytes.every((byte, i) => byte === b.bytes[i]);
}
23 changes: 23 additions & 0 deletions ts/shielder-sdk-crypto/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "ES2023",
"lib": ["ES2020", "WebWorker", "DOM"],
"module": "ESNext",
"moduleResolution": "bundler",
"declaration": true,
"outDir": "./dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"strictNullChecks": true,
"resolveJsonModule": true,
"baseUrl": ".",
"rootDir": "./src",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src", "tests"],
"exclude": ["dist", "node_modules"]
}

0 comments on commit 22d941a

Please sign in to comment.