From 1177469981532b4bdca6db17635ac2ddef9c0d70 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Mon, 24 Jun 2024 16:31:32 -0700 Subject: [PATCH 01/33] Add Mina contracts for EVM->Mina DelegationOrder proofs --- package-lock.json | 73 +++++++++++ .../contracts/mina-contracts/package.json | 30 +++++ .../contracts/mina-contracts/src/delegate.ts | 122 ++++++++++++++++++ .../contracts/mina-contracts/src/index.ts | 7 + .../contracts/mina-contracts/tsconfig.json | 8 ++ tsconfig.base.json | 1 + 6 files changed, 241 insertions(+) create mode 100644 packages/contracts/mina-contracts/package.json create mode 100644 packages/contracts/mina-contracts/src/delegate.ts create mode 100644 packages/contracts/mina-contracts/src/index.ts create mode 100644 packages/contracts/mina-contracts/tsconfig.json diff --git a/package-lock.json b/package-lock.json index c6ff49cf1..fa2549c68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5654,6 +5654,10 @@ "resolved": "packages/engine/paima-funnel", "link": true }, + "node_modules/@paima/mina-contracts": { + "resolved": "packages/contracts/mina-contracts", + "link": true + }, "node_modules/@paima/mw-core": { "resolved": "packages/paima-sdk/paima-mw-core", "link": true @@ -11830,6 +11834,15 @@ "node": ">=8" } }, + "node_modules/cachedir": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", + "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -13993,6 +14006,13 @@ "strip-bom": "^3.0.0" } }, + "node_modules/eslint-plugin-o1js": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-o1js/-/eslint-plugin-o1js-0.4.0.tgz", + "integrity": "sha512-12qI6OvAMtUIh8x9lB5uVzJbRMSR6tGrbCRM98fcCmll1FNvVSUIaat3CWhH17tkcjoyVSaFy0I/WzZcqPqaUA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/eslint-plugin-prettier": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", @@ -17457,6 +17477,16 @@ "node": ">=0.10.0" } }, + "node_modules/isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -19784,6 +19814,32 @@ "node": "*" } }, + "node_modules/o1js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/o1js/-/o1js-1.3.1.tgz", + "integrity": "sha512-FOhkN0/byoWm/Q0F+2y1eHTeKYy22Lcx1oIn4xITLWPsEX6s01B79qP0pJQBPKZW+ev0xmmY/8Jf2pBuWA53fQ==", + "license": "Apache-2.0", + "dependencies": { + "blakejs": "1.2.1", + "cachedir": "^2.4.0", + "isomorphic-fetch": "^3.0.0", + "js-sha256": "^0.9.0", + "reflect-metadata": "^0.1.13", + "tslib": "^2.3.0" + }, + "bin": { + "snarky-run": "src/build/run.js" + }, + "engines": { + "node": ">=18.14.0" + } + }, + "node_modules/o1js/node_modules/reflect-metadata": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", + "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", + "license": "Apache-2.0" + }, "node_modules/oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -26480,6 +26536,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "license": "MIT" + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -26995,6 +27057,17 @@ "hardhat": "^2.19.3" } }, + "packages/contracts/mina-contracts": { + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "o1js": "^1.3.1" + }, + "devDependencies": { + "eslint-plugin-o1js": "^0.4.0", + "typescript": "^5.3.3" + } + }, "packages/engine/paima-funnel": { "name": "@paima/funnel", "version": "1.0.0", diff --git a/packages/contracts/mina-contracts/package.json b/packages/contracts/mina-contracts/package.json new file mode 100644 index 000000000..0b8de07d4 --- /dev/null +++ b/packages/contracts/mina-contracts/package.json @@ -0,0 +1,30 @@ +{ + "name": "@paima/mina-contracts", + "version": "0.0.0", + "description": "Mina programs and contracts for the Paima ecosystem", + "author": "Paima Studios", + "license": "MIT", + "type": "module", + "exports": { + ".": "./build/src/index.js" + }, + "scripts": { + "build": "tsc", + "lint": "eslint src/", + "lint:fix": "npm run lint -- --fix", + "prestart": "npm install --silent && npm run build && docker compose up --wait", + "start": "node --enable-source-maps build/src/run.js" + }, + "keywords": [ + "mina", + "zk", + "o1js" + ], + "dependencies": { + "o1js": "^1.3.1" + }, + "devDependencies": { + "eslint-plugin-o1js": "^0.4.0", + "typescript": "^5.3.3" + } +} diff --git a/packages/contracts/mina-contracts/src/delegate.ts b/packages/contracts/mina-contracts/src/delegate.ts new file mode 100644 index 000000000..3a1981d6a --- /dev/null +++ b/packages/contracts/mina-contracts/src/delegate.ts @@ -0,0 +1,122 @@ +import { + Bool, + Bytes, + Crypto, + Poseidon, + PublicKey, + Struct, + UInt8, + ZkProgram, + createEcdsa, + createForeignCurve, +} from 'o1js'; + +// ---------------------------------------------------------------------------- +// Common data types + +/** A Mina foreign curve for Secp256k1, like Ethereum uses. */ +export class Secp256k1 extends createForeignCurve(Crypto.CurveParams.Secp256k1) { + /** Convert a standard 0x04{128 hex digits} public key into this provable struct. */ + static fromHex(publicKey: `0x${string}`): Secp256k1 { + if (!publicKey.startsWith('0x04') || publicKey.length != 4 + 64 + 64) { + throw new Error('Bad public key format'); + } + return Secp256k1.from({ + x: BigInt('0x' + publicKey.substring(4, 4 + 64)), + y: BigInt('0x' + publicKey.substring(4 + 64, 4 + 64 + 64)), + }); + } +} + +/** A Mina-provable ECDSA signature on the Secp256k1 curve, like Ethereum uses. */ +export class Ecdsa extends createEcdsa(Secp256k1) { + // o1js-provided fromHex is good enough +} + +// Ethereum's fixed prefix. +const ethereumPrefix = Bytes.fromString('\x19Ethereum Signed Message:\n'); +// A prefix to distinguish this delegation order scheme from what might be +// similar-looking messages. +const delegationPrefix = Bytes.fromString('DelegateMinaAddr:'); + +/** + * An order that a particular EVM address has signed to authorize (delegate) + * a Mina address to act on its behalf. + */ +export class DelegationOrder extends Struct({ + /** Mina public key that the delegation order is issued for. */ + target: PublicKey, + /** Ethereum public key that signed the delegation order. */ + signer: Secp256k1.provable, +}) { + private _innerMessage(): Bytes { + return Bytes.from([...delegationPrefix.bytes, ...encodeKey(this.target)]); + } + + /** Get the message for an Etherum wallet to sign, WITHOUT the Ethereum prefix. */ + bytesToSign(): Uint8Array { + return this._innerMessage().toBytes(); + } + + /** Validate that the given Ethereum signature matches this order, WITH the Ethereum prefix. */ + assertSignatureMatches(signature: Ecdsa) { + const inner = this._innerMessage(); + const fullMessage = Bytes.from([ + ...ethereumPrefix.bytes, + ...Bytes.fromString(String(inner.length)).bytes, + ...inner.bytes, + ]); + signature.verifyV2(fullMessage, this.signer).assertTrue(); + } + + /** Hash this entire order for use as a MerkleMap key. */ + hash() { + return Poseidon.hashWithPrefix('DelegationOrder', [ + ...this.target.toFields(), + ...this.signer.x.toFields(), + ...this.signer.y.toFields(), + ]); + } +} + +function encodeKey(k: PublicKey): UInt8[] { + const bytes = [boolToU8(k.isOdd)]; + const bits = k.x.toBits(/* implied 254 */); + for (let i = 0; i < bits.length; i += 8) { + let value = new UInt8(0); + for (let j = 0; j < 8; j++) { + value = value.mul(2).add(boolToU8(bits[i + j] ?? Bool(false))); + } + bytes.push(value); + } + return bytes; +} + +function boolToU8(bool: Bool): UInt8 { + return UInt8.from(bool.toField()); +} + +// ---------------------------------------------------------------------------- +// The provable program itself + +/** + * A simple {@link ZkProgram} that proves that a valid signature exists for an + * input {@link DelegationOrder}. + */ +export const DelegationOrderProgram = ZkProgram({ + name: 'DelegationOrderProgram', + + publicInput: DelegationOrder, + + methods: { + sign: { + privateInputs: [Ecdsa.provable], + + async method(order: DelegationOrder, signature: Ecdsa) { + order.assertSignatureMatches(signature); + }, + }, + }, +}); +/** A verifiable proof of {@link DelegationOrderProgram}'s success. */ +export class DelegationOrderProof extends ZkProgram.Proof(DelegationOrderProgram) {} diff --git a/packages/contracts/mina-contracts/src/index.ts b/packages/contracts/mina-contracts/src/index.ts new file mode 100644 index 000000000..02889c877 --- /dev/null +++ b/packages/contracts/mina-contracts/src/index.ts @@ -0,0 +1,7 @@ +export { + Secp256k1, + Ecdsa, + DelegationOrder, + DelegationOrderProgram, + DelegationOrderProof, +} from './delegate.js'; diff --git a/packages/contracts/mina-contracts/tsconfig.json b/packages/contracts/mina-contracts/tsconfig.json new file mode 100644 index 000000000..2d8201c8f --- /dev/null +++ b/packages/contracts/mina-contracts/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build", + }, + "include": ["./src"], +} diff --git a/tsconfig.base.json b/tsconfig.base.json index 74a119f8f..4f45116b4 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -39,6 +39,7 @@ "@paima/crypto/*": ["./packages/paima-sdk/paima-crypto/*"], "@paima/db/*": ["./packages/node-sdk/paima-db/*"], "@paima/evm-contracts/*": ["./packages/contracts/evm-contracts/*"], + "@paima/mina-contracts/*": ["./packages/contracts/mina-contracts/*"], "@paima/executors/*": ["./packages/paima-sdk/paima-executors/*"], "@paima/funnel/*": ["./packages/engine/paima-funnel/*"], "@paima/mw-core/*": ["./packages/paima-sdk/paima-mw-core/*"], From 5dfef13d282c564a06f0bea7843528c00257de57 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Mon, 24 Jun 2024 16:58:11 -0700 Subject: [PATCH 02/33] Add first PrebuiltCache version to improve load times --- .../contracts/mina-contracts/package.json | 6 +++- .../contracts/mina-contracts/src/cache.ts | 34 +++++++++++++++++++ .../contracts/mina-contracts/src/delegate.ts | 4 +++ .../contracts/mina-contracts/src/postbuild.ts | 10 ++++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 packages/contracts/mina-contracts/src/cache.ts create mode 100644 packages/contracts/mina-contracts/src/postbuild.ts diff --git a/packages/contracts/mina-contracts/package.json b/packages/contracts/mina-contracts/package.json index 0b8de07d4..5b0b54005 100644 --- a/packages/contracts/mina-contracts/package.json +++ b/packages/contracts/mina-contracts/package.json @@ -6,10 +6,14 @@ "license": "MIT", "type": "module", "exports": { - ".": "./build/src/index.js" + ".": "./build/index.js" }, + "files": [ + "build" + ], "scripts": { "build": "tsc", + "postbuild": "node build/postbuild.js", "lint": "eslint src/", "lint:fix": "npm run lint -- --fix", "prestart": "npm install --silent && npm run build && docker compose up --wait", diff --git a/packages/contracts/mina-contracts/src/cache.ts b/packages/contracts/mina-contracts/src/cache.ts new file mode 100644 index 000000000..344ec9655 --- /dev/null +++ b/packages/contracts/mina-contracts/src/cache.ts @@ -0,0 +1,34 @@ +import { Cache, CacheHeader, Field } from 'o1js'; + +type CompileFn = ( + options?: { cache?: Cache | undefined; forceRecompile?: boolean | undefined } | undefined +) => Promise<{ verificationKey: { data: string; hash: Field } }>; + +export class PrebuiltCache implements Cache { + read(header: CacheHeader): Uint8Array | undefined { + const result = Cache.FileSystem(__dirname + '/zkcache').read(header); + if (!result) { + console.warn('PrebuiltCache miss for', header); + } + return result; + } + write(header: CacheHeader, value: Uint8Array): void { + throw new Error('PrebuiltCache cannot write.'); + } + canWrite: boolean = false; + debug?: boolean | undefined = false; + + static readonly INSTANCE = new PrebuiltCache(); + + /** Wrap a .compile function to change its default cache to PrebuiltCache. */ + static wrap(original: CompileFn): CompileFn { + return function compile(options) { + return original({ + // Default cache to the PrebuiltCache instead of o1js FileSystemDefault, + // but the user can still override it. + cache: options?.cache ?? PrebuiltCache.INSTANCE, + ...options, + }); + }; + } +} diff --git a/packages/contracts/mina-contracts/src/delegate.ts b/packages/contracts/mina-contracts/src/delegate.ts index 3a1981d6a..eb1eda32a 100644 --- a/packages/contracts/mina-contracts/src/delegate.ts +++ b/packages/contracts/mina-contracts/src/delegate.ts @@ -10,6 +10,7 @@ import { createEcdsa, createForeignCurve, } from 'o1js'; +import { PrebuiltCache } from './cache'; // ---------------------------------------------------------------------------- // Common data types @@ -118,5 +119,8 @@ export const DelegationOrderProgram = ZkProgram({ }, }, }); + +DelegationOrderProgram.compile = PrebuiltCache.wrap(DelegationOrderProgram.compile); + /** A verifiable proof of {@link DelegationOrderProgram}'s success. */ export class DelegationOrderProof extends ZkProgram.Proof(DelegationOrderProgram) {} diff --git a/packages/contracts/mina-contracts/src/postbuild.ts b/packages/contracts/mina-contracts/src/postbuild.ts new file mode 100644 index 000000000..1b49e8676 --- /dev/null +++ b/packages/contracts/mina-contracts/src/postbuild.ts @@ -0,0 +1,10 @@ +import { Cache } from 'o1js'; +import { DelegationOrderProgram } from './delegate.js'; + +const cache = Cache.FileSystem('build/zkcache'); +for (const program of [DelegationOrderProgram]) { + program.compile({ + cache: cache, + forceRecompile: false, + }); +} From c320e600b82170317a90c680523b2a98211030ae Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Tue, 25 Jun 2024 15:51:21 -0700 Subject: [PATCH 03/33] Add stub test.ts --- packages/contracts/mina-contracts/package.json | 6 ++---- packages/contracts/mina-contracts/src/cache.ts | 2 +- packages/contracts/mina-contracts/src/delegate.ts | 6 +++--- packages/contracts/mina-contracts/src/test.ts | 1 + 4 files changed, 7 insertions(+), 8 deletions(-) create mode 100644 packages/contracts/mina-contracts/src/test.ts diff --git a/packages/contracts/mina-contracts/package.json b/packages/contracts/mina-contracts/package.json index 5b0b54005..5148e8e05 100644 --- a/packages/contracts/mina-contracts/package.json +++ b/packages/contracts/mina-contracts/package.json @@ -14,10 +14,8 @@ "scripts": { "build": "tsc", "postbuild": "node build/postbuild.js", - "lint": "eslint src/", - "lint:fix": "npm run lint -- --fix", - "prestart": "npm install --silent && npm run build && docker compose up --wait", - "start": "node --enable-source-maps build/src/run.js" + "pretest": "npm run build", + "test": "node build/test.js" }, "keywords": [ "mina", diff --git a/packages/contracts/mina-contracts/src/cache.ts b/packages/contracts/mina-contracts/src/cache.ts index 344ec9655..d90f83e0c 100644 --- a/packages/contracts/mina-contracts/src/cache.ts +++ b/packages/contracts/mina-contracts/src/cache.ts @@ -26,7 +26,7 @@ export class PrebuiltCache implements Cache { return original({ // Default cache to the PrebuiltCache instead of o1js FileSystemDefault, // but the user can still override it. - cache: options?.cache ?? PrebuiltCache.INSTANCE, + cache: PrebuiltCache.INSTANCE, ...options, }); }; diff --git a/packages/contracts/mina-contracts/src/delegate.ts b/packages/contracts/mina-contracts/src/delegate.ts index eb1eda32a..466e97d68 100644 --- a/packages/contracts/mina-contracts/src/delegate.ts +++ b/packages/contracts/mina-contracts/src/delegate.ts @@ -10,7 +10,7 @@ import { createEcdsa, createForeignCurve, } from 'o1js'; -import { PrebuiltCache } from './cache'; +import { PrebuiltCache } from './cache.js'; // ---------------------------------------------------------------------------- // Common data types @@ -81,8 +81,8 @@ export class DelegationOrder extends Struct({ } function encodeKey(k: PublicKey): UInt8[] { - const bytes = [boolToU8(k.isOdd)]; - const bits = k.x.toBits(/* implied 254 */); + const bytes = []; + const bits = [...k.x.toBits(254), k.isOdd]; for (let i = 0; i < bits.length; i += 8) { let value = new UInt8(0); for (let j = 0; j < 8; j++) { diff --git a/packages/contracts/mina-contracts/src/test.ts b/packages/contracts/mina-contracts/src/test.ts new file mode 100644 index 000000000..e900661a7 --- /dev/null +++ b/packages/contracts/mina-contracts/src/test.ts @@ -0,0 +1 @@ +import { DelegationOrder, DelegationOrderProgram } from './index.js'; From 9ea81b20f832c0f492ad40a9aab2eefc62283376 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Tue, 25 Jun 2024 16:25:18 -0700 Subject: [PATCH 04/33] Add test that verifies prebuilt cache works --- package-lock.json | 5 +++-- packages/contracts/mina-contracts/README.md | 3 +++ packages/contracts/mina-contracts/package.json | 6 ++++-- packages/contracts/mina-contracts/src/cache.ts | 14 ++++++++++++-- packages/contracts/mina-contracts/src/postbuild.ts | 4 +++- packages/contracts/mina-contracts/src/test.ts | 10 +++++++++- 6 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 packages/contracts/mina-contracts/README.md diff --git a/package-lock.json b/package-lock.json index fa2549c68..5cd754d3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27034,7 +27034,7 @@ }, "packages/contracts/evm-contracts": { "name": "@paima/evm-contracts", - "version": "3.0.0", + "version": "3.1.0", "license": "MIT", "dependencies": { "dotenv": "^16.3.1" @@ -27058,7 +27058,8 @@ } }, "packages/contracts/mina-contracts": { - "version": "0.0.0", + "name": "@paima/mina-contracts", + "version": "3.1.0", "license": "MIT", "dependencies": { "o1js": "^1.3.1" diff --git a/packages/contracts/mina-contracts/README.md b/packages/contracts/mina-contracts/README.md new file mode 100644 index 000000000..048235220 --- /dev/null +++ b/packages/contracts/mina-contracts/README.md @@ -0,0 +1,3 @@ +# Paima Mina contracts + +NPM package for Mina programs and contracts for Paima Engine and related utilities. diff --git a/packages/contracts/mina-contracts/package.json b/packages/contracts/mina-contracts/package.json index 5148e8e05..ff37aedee 100644 --- a/packages/contracts/mina-contracts/package.json +++ b/packages/contracts/mina-contracts/package.json @@ -1,6 +1,6 @@ { "name": "@paima/mina-contracts", - "version": "0.0.0", + "version": "3.1.0", "description": "Mina programs and contracts for the Paima ecosystem", "author": "Paima Studios", "license": "MIT", @@ -9,7 +9,9 @@ ".": "./build/index.js" }, "files": [ - "build" + "build", + "!build/postbuild.*", + "!build/test.*" ], "scripts": { "build": "tsc", diff --git a/packages/contracts/mina-contracts/src/cache.ts b/packages/contracts/mina-contracts/src/cache.ts index d90f83e0c..cad24af07 100644 --- a/packages/contracts/mina-contracts/src/cache.ts +++ b/packages/contracts/mina-contracts/src/cache.ts @@ -1,18 +1,28 @@ import { Cache, CacheHeader, Field } from 'o1js'; +import path from 'path'; +import { fileURLToPath } from 'url'; type CompileFn = ( options?: { cache?: Cache | undefined; forceRecompile?: boolean | undefined } | undefined ) => Promise<{ verificationKey: { data: string; hash: Field } }>; +const baseCache = Cache.FileSystem(path.dirname(fileURLToPath(import.meta.url)) + '/zkcache'); + export class PrebuiltCache implements Cache { read(header: CacheHeader): Uint8Array | undefined { - const result = Cache.FileSystem(__dirname + '/zkcache').read(header); - if (!result) { + if (this.debug) { + console.log('PrebuiltCache.read', header.persistentId); + } + const result = baseCache.read(header); + if (!result && this.debug) { console.warn('PrebuiltCache miss for', header); } return result; } write(header: CacheHeader, value: Uint8Array): void { + if (this.debug) { + console.log('PrebuiltCache.write', header.persistentId); + } throw new Error('PrebuiltCache cannot write.'); } canWrite: boolean = false; diff --git a/packages/contracts/mina-contracts/src/postbuild.ts b/packages/contracts/mina-contracts/src/postbuild.ts index 1b49e8676..23ccf88c1 100644 --- a/packages/contracts/mina-contracts/src/postbuild.ts +++ b/packages/contracts/mina-contracts/src/postbuild.ts @@ -3,8 +3,10 @@ import { DelegationOrderProgram } from './delegate.js'; const cache = Cache.FileSystem('build/zkcache'); for (const program of [DelegationOrderProgram]) { - program.compile({ + console.time(program.name); + await program.compile({ cache: cache, forceRecompile: false, }); + console.timeEnd(program.name); } diff --git a/packages/contracts/mina-contracts/src/test.ts b/packages/contracts/mina-contracts/src/test.ts index e900661a7..806a43888 100644 --- a/packages/contracts/mina-contracts/src/test.ts +++ b/packages/contracts/mina-contracts/src/test.ts @@ -1 +1,9 @@ -import { DelegationOrder, DelegationOrderProgram } from './index.js'; +import { PrebuiltCache } from './cache.js'; +import { DelegationOrderProgram } from './index.js'; + +// Simple test that PrebuiltCache succeeds. + +// Set debug=true so exceptions in read() get logged to console. +PrebuiltCache.INSTANCE.debug = true; + +await DelegationOrderProgram.compile(); From c5b365eb38c3fae3451ef759ab54cf92687cb8c6 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Wed, 26 Jun 2024 12:30:39 -0700 Subject: [PATCH 05/33] Prebuilt: support SmartContract, test better, clean up --- .../contracts/mina-contracts/src/cache.ts | 28 ++++++--- .../contracts/mina-contracts/src/index.ts | 5 +- .../contracts/mina-contracts/src/postbuild.ts | 62 +++++++++++++++---- packages/contracts/mina-contracts/src/test.ts | 23 ++++++- .../contracts/mina-contracts/tsconfig.json | 4 ++ 5 files changed, 98 insertions(+), 24 deletions(-) diff --git a/packages/contracts/mina-contracts/src/cache.ts b/packages/contracts/mina-contracts/src/cache.ts index cad24af07..30d20b66b 100644 --- a/packages/contracts/mina-contracts/src/cache.ts +++ b/packages/contracts/mina-contracts/src/cache.ts @@ -2,20 +2,29 @@ import { Cache, CacheHeader, Field } from 'o1js'; import path from 'path'; import { fileURLToPath } from 'url'; -type CompileFn = ( +type CompileFn = ( options?: { cache?: Cache | undefined; forceRecompile?: boolean | undefined } | undefined -) => Promise<{ verificationKey: { data: string; hash: Field } }>; +) => Promise; const baseCache = Cache.FileSystem(path.dirname(fileURLToPath(import.meta.url)) + '/zkcache'); +/** + * An o1js-compatible {@link Cache} that uses circuit data compiled at build + * time instead of run time. + */ export class PrebuiltCache implements Cache { read(header: CacheHeader): Uint8Array | undefined { if (this.debug) { console.log('PrebuiltCache.read', header.persistentId); } const result = baseCache.read(header); - if (!result && this.debug) { - console.warn('PrebuiltCache miss for', header); + if (result) { + this.hits.add(header.persistentId); + } else { + this.misses.add(header.persistentId); + if (this.debug) { + console.warn('PrebuiltCache miss for', header); + } } return result; } @@ -25,15 +34,18 @@ export class PrebuiltCache implements Cache { } throw new Error('PrebuiltCache cannot write.'); } - canWrite: boolean = false; + readonly canWrite: boolean = false; debug?: boolean | undefined = false; + hits = new Set(); + misses = new Set(); + static readonly INSTANCE = new PrebuiltCache(); /** Wrap a .compile function to change its default cache to PrebuiltCache. */ - static wrap(original: CompileFn): CompileFn { - return function compile(options) { - return original({ + static wrap(original: CompileFn): CompileFn { + return function compile(this: unknown, options) { + return original.call(this, { // Default cache to the PrebuiltCache instead of o1js FileSystemDefault, // but the user can still override it. cache: PrebuiltCache.INSTANCE, diff --git a/packages/contracts/mina-contracts/src/index.ts b/packages/contracts/mina-contracts/src/index.ts index 02889c877..721505df1 100644 --- a/packages/contracts/mina-contracts/src/index.ts +++ b/packages/contracts/mina-contracts/src/index.ts @@ -1,7 +1,8 @@ +export { PrebuiltCache } from './cache.js'; export { - Secp256k1, - Ecdsa, DelegationOrder, DelegationOrderProgram, DelegationOrderProof, + Ecdsa, + Secp256k1, } from './delegate.js'; diff --git a/packages/contracts/mina-contracts/src/postbuild.ts b/packages/contracts/mina-contracts/src/postbuild.ts index 23ccf88c1..563f37e8e 100644 --- a/packages/contracts/mina-contracts/src/postbuild.ts +++ b/packages/contracts/mina-contracts/src/postbuild.ts @@ -1,12 +1,52 @@ -import { Cache } from 'o1js'; -import { DelegationOrderProgram } from './delegate.js'; - -const cache = Cache.FileSystem('build/zkcache'); -for (const program of [DelegationOrderProgram]) { - console.time(program.name); - await program.compile({ - cache: cache, - forceRecompile: false, - }); - console.timeEnd(program.name); +import { Cache, CacheHeader } from 'o1js'; +import * as everything from './index.js'; +import { readdir, unlink } from 'fs/promises'; + +const OUTPUT_DIR = 'build/zkcache'; + +class TrackingCache implements Cache { + inner: Cache; + files = new Set(); + + constructor(inner: Cache) { + this.inner = inner; + } + + read(header: CacheHeader): Uint8Array | undefined { + this.files.add(`${header.persistentId}.header`); + this.files.add(header.persistentId); + return this.inner.read(header); + } + write(header: CacheHeader, value: Uint8Array): void { + this.files.add(`${header.persistentId}.header`); + this.files.add(header.persistentId); + return this.inner.write(header, value); + } + get canWrite() { + return this.inner.canWrite; + } + get debug() { + return this.inner.debug; + } +} + +const cache = new TrackingCache(Cache.FileSystem(OUTPUT_DIR)); + +// compile() every exported ZkProgram and SmartContract into build/zkcache/. +for (const program of Object.values(everything)) { + if ('compile' in program) { + console.time(program.name); + await program.compile({ + cache: cache, + forceRecompile: false, + }); + console.timeEnd(program.name); + } +} + +// Delete anything that doesn't belong anymore. +for (const file of await readdir(OUTPUT_DIR)) { + if (!cache.files.has(file)) { + await unlink(`${OUTPUT_DIR}/${file}`); + } } diff --git a/packages/contracts/mina-contracts/src/test.ts b/packages/contracts/mina-contracts/src/test.ts index 806a43888..a9e3f4070 100644 --- a/packages/contracts/mina-contracts/src/test.ts +++ b/packages/contracts/mina-contracts/src/test.ts @@ -1,9 +1,26 @@ +import * as everything from './index.js'; import { PrebuiltCache } from './cache.js'; -import { DelegationOrderProgram } from './index.js'; -// Simple test that PrebuiltCache succeeds. +// Simple test that PrebuiltCache is both in-use and succeeds on everything. // Set debug=true so exceptions in read() get logged to console. PrebuiltCache.INSTANCE.debug = true; -await DelegationOrderProgram.compile(); +for (const program of Object.values(everything)) { + if ('compile' in program) { + console.log(program.name); + PrebuiltCache.INSTANCE.hits.clear(); + + // To "really" test what users will experience, don't pass anything to .compile() here. + await program.compile(); + + if (PrebuiltCache.INSTANCE.hits.size == 0) { + PrebuiltCache.INSTANCE.misses.add(`${program.name}.compile() not wrapped`); + } + } +} + +if (PrebuiltCache.INSTANCE.misses.size > 0) { + console.error('PrebuiltCache missed:', ...PrebuiltCache.INSTANCE.misses); + process.exit(1); +} diff --git a/packages/contracts/mina-contracts/tsconfig.json b/packages/contracts/mina-contracts/tsconfig.json index 2d8201c8f..34725b1ba 100644 --- a/packages/contracts/mina-contracts/tsconfig.json +++ b/packages/contracts/mina-contracts/tsconfig.json @@ -3,6 +3,10 @@ "compilerOptions": { "rootDir": "src", "outDir": "build", + + // TS decorator metadata is necessary for SmartContracts to compile. + "experimentalDecorators": true, + "emitDecoratorMetadata": true, }, "include": ["./src"], } From 6c2d72989a5c23ed3ebd715b8e168eced3e5fac2 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Wed, 26 Jun 2024 12:56:01 -0700 Subject: [PATCH 06/33] Restore original shorter delegation prefix --- packages/contracts/mina-contracts/src/delegate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/mina-contracts/src/delegate.ts b/packages/contracts/mina-contracts/src/delegate.ts index 466e97d68..135174a21 100644 --- a/packages/contracts/mina-contracts/src/delegate.ts +++ b/packages/contracts/mina-contracts/src/delegate.ts @@ -38,7 +38,7 @@ export class Ecdsa extends createEcdsa(Secp256k1) { const ethereumPrefix = Bytes.fromString('\x19Ethereum Signed Message:\n'); // A prefix to distinguish this delegation order scheme from what might be // similar-looking messages. -const delegationPrefix = Bytes.fromString('DelegateMinaAddr:'); +const delegationPrefix = Bytes.fromString('MinaDelegate|'); /** * An order that a particular EVM address has signed to authorize (delegate) From 35de3071841308c367220782c1d6c346046ff775 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Wed, 26 Jun 2024 14:18:26 -0700 Subject: [PATCH 07/33] Skip removal and run twice because apparently you have to do that --- .../contracts/mina-contracts/package.json | 2 +- .../contracts/mina-contracts/src/cache.ts | 1 + .../contracts/mina-contracts/src/postbuild.ts | 39 +------------------ 3 files changed, 4 insertions(+), 38 deletions(-) diff --git a/packages/contracts/mina-contracts/package.json b/packages/contracts/mina-contracts/package.json index ff37aedee..ffd76a17a 100644 --- a/packages/contracts/mina-contracts/package.json +++ b/packages/contracts/mina-contracts/package.json @@ -15,7 +15,7 @@ ], "scripts": { "build": "tsc", - "postbuild": "node build/postbuild.js", + "postbuild": "node build/postbuild.js && node build/postbuild.js", "pretest": "npm run build", "test": "node build/test.js" }, diff --git a/packages/contracts/mina-contracts/src/cache.ts b/packages/contracts/mina-contracts/src/cache.ts index 30d20b66b..a3d40000a 100644 --- a/packages/contracts/mina-contracts/src/cache.ts +++ b/packages/contracts/mina-contracts/src/cache.ts @@ -6,6 +6,7 @@ type CompileFn = ( options?: { cache?: Cache | undefined; forceRecompile?: boolean | undefined } | undefined ) => Promise; +// TODO: web support const baseCache = Cache.FileSystem(path.dirname(fileURLToPath(import.meta.url)) + '/zkcache'); /** diff --git a/packages/contracts/mina-contracts/src/postbuild.ts b/packages/contracts/mina-contracts/src/postbuild.ts index 563f37e8e..45fb851b8 100644 --- a/packages/contracts/mina-contracts/src/postbuild.ts +++ b/packages/contracts/mina-contracts/src/postbuild.ts @@ -1,36 +1,8 @@ -import { Cache, CacheHeader } from 'o1js'; +import { Cache } from 'o1js'; import * as everything from './index.js'; -import { readdir, unlink } from 'fs/promises'; const OUTPUT_DIR = 'build/zkcache'; - -class TrackingCache implements Cache { - inner: Cache; - files = new Set(); - - constructor(inner: Cache) { - this.inner = inner; - } - - read(header: CacheHeader): Uint8Array | undefined { - this.files.add(`${header.persistentId}.header`); - this.files.add(header.persistentId); - return this.inner.read(header); - } - write(header: CacheHeader, value: Uint8Array): void { - this.files.add(`${header.persistentId}.header`); - this.files.add(header.persistentId); - return this.inner.write(header, value); - } - get canWrite() { - return this.inner.canWrite; - } - get debug() { - return this.inner.debug; - } -} - -const cache = new TrackingCache(Cache.FileSystem(OUTPUT_DIR)); +const cache = Cache.FileSystem(OUTPUT_DIR); // compile() every exported ZkProgram and SmartContract into build/zkcache/. for (const program of Object.values(everything)) { @@ -43,10 +15,3 @@ for (const program of Object.values(everything)) { console.timeEnd(program.name); } } - -// Delete anything that doesn't belong anymore. -for (const file of await readdir(OUTPUT_DIR)) { - if (!cache.files.has(file)) { - await unlink(`${OUTPUT_DIR}/${file}`); - } -} From 62759d3de2712541513e56b069a943c2f80c8550 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Fri, 28 Jun 2024 12:40:09 -0700 Subject: [PATCH 08/33] Harmonize signed message prefix with rest of Paima --- packages/contracts/mina-contracts/src/delegate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/mina-contracts/src/delegate.ts b/packages/contracts/mina-contracts/src/delegate.ts index 135174a21..8e1a2b0e9 100644 --- a/packages/contracts/mina-contracts/src/delegate.ts +++ b/packages/contracts/mina-contracts/src/delegate.ts @@ -38,7 +38,7 @@ export class Ecdsa extends createEcdsa(Secp256k1) { const ethereumPrefix = Bytes.fromString('\x19Ethereum Signed Message:\n'); // A prefix to distinguish this delegation order scheme from what might be // similar-looking messages. -const delegationPrefix = Bytes.fromString('MinaDelegate|'); +const delegationPrefix = Bytes.fromString('DELEGATE-WALLET:'); /** * An order that a particular EVM address has signed to authorize (delegate) From d9b681633c8f064523db13910a9d66a2ec29071d Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Fri, 28 Jun 2024 16:53:20 -0700 Subject: [PATCH 09/33] Rename package to mina-delegation --- .../contracts/{mina-contracts => mina-delegation}/README.md | 0 .../{mina-contracts => mina-delegation}/package.json | 4 ++-- .../{mina-contracts => mina-delegation}/src/cache.ts | 0 .../{mina-contracts => mina-delegation}/src/delegate.ts | 0 .../{mina-contracts => mina-delegation}/src/index.ts | 0 .../{mina-contracts => mina-delegation}/src/postbuild.ts | 0 .../contracts/{mina-contracts => mina-delegation}/src/test.ts | 0 .../{mina-contracts => mina-delegation}/tsconfig.json | 0 8 files changed, 2 insertions(+), 2 deletions(-) rename packages/contracts/{mina-contracts => mina-delegation}/README.md (100%) rename packages/contracts/{mina-contracts => mina-delegation}/package.json (84%) rename packages/contracts/{mina-contracts => mina-delegation}/src/cache.ts (100%) rename packages/contracts/{mina-contracts => mina-delegation}/src/delegate.ts (100%) rename packages/contracts/{mina-contracts => mina-delegation}/src/index.ts (100%) rename packages/contracts/{mina-contracts => mina-delegation}/src/postbuild.ts (100%) rename packages/contracts/{mina-contracts => mina-delegation}/src/test.ts (100%) rename packages/contracts/{mina-contracts => mina-delegation}/tsconfig.json (100%) diff --git a/packages/contracts/mina-contracts/README.md b/packages/contracts/mina-delegation/README.md similarity index 100% rename from packages/contracts/mina-contracts/README.md rename to packages/contracts/mina-delegation/README.md diff --git a/packages/contracts/mina-contracts/package.json b/packages/contracts/mina-delegation/package.json similarity index 84% rename from packages/contracts/mina-contracts/package.json rename to packages/contracts/mina-delegation/package.json index ffd76a17a..f86619e75 100644 --- a/packages/contracts/mina-contracts/package.json +++ b/packages/contracts/mina-delegation/package.json @@ -1,7 +1,7 @@ { - "name": "@paima/mina-contracts", + "name": "@paima/mina-delegation", "version": "3.1.0", - "description": "Mina programs and contracts for the Paima ecosystem", + "description": "Mina ZkProgram for EVM->Mina delegation", "author": "Paima Studios", "license": "MIT", "type": "module", diff --git a/packages/contracts/mina-contracts/src/cache.ts b/packages/contracts/mina-delegation/src/cache.ts similarity index 100% rename from packages/contracts/mina-contracts/src/cache.ts rename to packages/contracts/mina-delegation/src/cache.ts diff --git a/packages/contracts/mina-contracts/src/delegate.ts b/packages/contracts/mina-delegation/src/delegate.ts similarity index 100% rename from packages/contracts/mina-contracts/src/delegate.ts rename to packages/contracts/mina-delegation/src/delegate.ts diff --git a/packages/contracts/mina-contracts/src/index.ts b/packages/contracts/mina-delegation/src/index.ts similarity index 100% rename from packages/contracts/mina-contracts/src/index.ts rename to packages/contracts/mina-delegation/src/index.ts diff --git a/packages/contracts/mina-contracts/src/postbuild.ts b/packages/contracts/mina-delegation/src/postbuild.ts similarity index 100% rename from packages/contracts/mina-contracts/src/postbuild.ts rename to packages/contracts/mina-delegation/src/postbuild.ts diff --git a/packages/contracts/mina-contracts/src/test.ts b/packages/contracts/mina-delegation/src/test.ts similarity index 100% rename from packages/contracts/mina-contracts/src/test.ts rename to packages/contracts/mina-delegation/src/test.ts diff --git a/packages/contracts/mina-contracts/tsconfig.json b/packages/contracts/mina-delegation/tsconfig.json similarity index 100% rename from packages/contracts/mina-contracts/tsconfig.json rename to packages/contracts/mina-delegation/tsconfig.json From 231c557a6bbab664b14c5c0fc7da363f12cb2815 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Wed, 3 Jul 2024 12:40:26 -0700 Subject: [PATCH 10/33] Trash cache, since 700M is unreasonable to put on NPM --- .../contracts/mina-delegation/package.json | 9 +-- .../contracts/mina-delegation/src/cache.ts | 57 ------------------- .../contracts/mina-delegation/src/delegate.ts | 3 - .../contracts/mina-delegation/src/index.ts | 1 - .../mina-delegation/src/postbuild.ts | 17 ------ .../contracts/mina-delegation/src/test.ts | 26 --------- 6 files changed, 2 insertions(+), 111 deletions(-) delete mode 100644 packages/contracts/mina-delegation/src/cache.ts delete mode 100644 packages/contracts/mina-delegation/src/postbuild.ts delete mode 100644 packages/contracts/mina-delegation/src/test.ts diff --git a/packages/contracts/mina-delegation/package.json b/packages/contracts/mina-delegation/package.json index f86619e75..b406c348a 100644 --- a/packages/contracts/mina-delegation/package.json +++ b/packages/contracts/mina-delegation/package.json @@ -9,15 +9,10 @@ ".": "./build/index.js" }, "files": [ - "build", - "!build/postbuild.*", - "!build/test.*" + "build" ], "scripts": { - "build": "tsc", - "postbuild": "node build/postbuild.js && node build/postbuild.js", - "pretest": "npm run build", - "test": "node build/test.js" + "build": "tsc" }, "keywords": [ "mina", diff --git a/packages/contracts/mina-delegation/src/cache.ts b/packages/contracts/mina-delegation/src/cache.ts deleted file mode 100644 index a3d40000a..000000000 --- a/packages/contracts/mina-delegation/src/cache.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Cache, CacheHeader, Field } from 'o1js'; -import path from 'path'; -import { fileURLToPath } from 'url'; - -type CompileFn = ( - options?: { cache?: Cache | undefined; forceRecompile?: boolean | undefined } | undefined -) => Promise; - -// TODO: web support -const baseCache = Cache.FileSystem(path.dirname(fileURLToPath(import.meta.url)) + '/zkcache'); - -/** - * An o1js-compatible {@link Cache} that uses circuit data compiled at build - * time instead of run time. - */ -export class PrebuiltCache implements Cache { - read(header: CacheHeader): Uint8Array | undefined { - if (this.debug) { - console.log('PrebuiltCache.read', header.persistentId); - } - const result = baseCache.read(header); - if (result) { - this.hits.add(header.persistentId); - } else { - this.misses.add(header.persistentId); - if (this.debug) { - console.warn('PrebuiltCache miss for', header); - } - } - return result; - } - write(header: CacheHeader, value: Uint8Array): void { - if (this.debug) { - console.log('PrebuiltCache.write', header.persistentId); - } - throw new Error('PrebuiltCache cannot write.'); - } - readonly canWrite: boolean = false; - debug?: boolean | undefined = false; - - hits = new Set(); - misses = new Set(); - - static readonly INSTANCE = new PrebuiltCache(); - - /** Wrap a .compile function to change its default cache to PrebuiltCache. */ - static wrap(original: CompileFn): CompileFn { - return function compile(this: unknown, options) { - return original.call(this, { - // Default cache to the PrebuiltCache instead of o1js FileSystemDefault, - // but the user can still override it. - cache: PrebuiltCache.INSTANCE, - ...options, - }); - }; - } -} diff --git a/packages/contracts/mina-delegation/src/delegate.ts b/packages/contracts/mina-delegation/src/delegate.ts index 8e1a2b0e9..3e0d48ea2 100644 --- a/packages/contracts/mina-delegation/src/delegate.ts +++ b/packages/contracts/mina-delegation/src/delegate.ts @@ -10,7 +10,6 @@ import { createEcdsa, createForeignCurve, } from 'o1js'; -import { PrebuiltCache } from './cache.js'; // ---------------------------------------------------------------------------- // Common data types @@ -120,7 +119,5 @@ export const DelegationOrderProgram = ZkProgram({ }, }); -DelegationOrderProgram.compile = PrebuiltCache.wrap(DelegationOrderProgram.compile); - /** A verifiable proof of {@link DelegationOrderProgram}'s success. */ export class DelegationOrderProof extends ZkProgram.Proof(DelegationOrderProgram) {} diff --git a/packages/contracts/mina-delegation/src/index.ts b/packages/contracts/mina-delegation/src/index.ts index 721505df1..4e44ce4bd 100644 --- a/packages/contracts/mina-delegation/src/index.ts +++ b/packages/contracts/mina-delegation/src/index.ts @@ -1,4 +1,3 @@ -export { PrebuiltCache } from './cache.js'; export { DelegationOrder, DelegationOrderProgram, diff --git a/packages/contracts/mina-delegation/src/postbuild.ts b/packages/contracts/mina-delegation/src/postbuild.ts deleted file mode 100644 index 45fb851b8..000000000 --- a/packages/contracts/mina-delegation/src/postbuild.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Cache } from 'o1js'; -import * as everything from './index.js'; - -const OUTPUT_DIR = 'build/zkcache'; -const cache = Cache.FileSystem(OUTPUT_DIR); - -// compile() every exported ZkProgram and SmartContract into build/zkcache/. -for (const program of Object.values(everything)) { - if ('compile' in program) { - console.time(program.name); - await program.compile({ - cache: cache, - forceRecompile: false, - }); - console.timeEnd(program.name); - } -} diff --git a/packages/contracts/mina-delegation/src/test.ts b/packages/contracts/mina-delegation/src/test.ts deleted file mode 100644 index a9e3f4070..000000000 --- a/packages/contracts/mina-delegation/src/test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as everything from './index.js'; -import { PrebuiltCache } from './cache.js'; - -// Simple test that PrebuiltCache is both in-use and succeeds on everything. - -// Set debug=true so exceptions in read() get logged to console. -PrebuiltCache.INSTANCE.debug = true; - -for (const program of Object.values(everything)) { - if ('compile' in program) { - console.log(program.name); - PrebuiltCache.INSTANCE.hits.clear(); - - // To "really" test what users will experience, don't pass anything to .compile() here. - await program.compile(); - - if (PrebuiltCache.INSTANCE.hits.size == 0) { - PrebuiltCache.INSTANCE.misses.add(`${program.name}.compile() not wrapped`); - } - } -} - -if (PrebuiltCache.INSTANCE.misses.size > 0) { - console.error('PrebuiltCache missed:', ...PrebuiltCache.INSTANCE.misses); - process.exit(1); -} From e153e16eba61aaa62c8d1f8ab821034a19beb00e Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Tue, 9 Jul 2024 09:58:46 -0700 Subject: [PATCH 11/33] Add 'main' and 'types' so old import schemes find us --- packages/contracts/mina-delegation/package.json | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/contracts/mina-delegation/package.json b/packages/contracts/mina-delegation/package.json index b406c348a..cd8d1f9dc 100644 --- a/packages/contracts/mina-delegation/package.json +++ b/packages/contracts/mina-delegation/package.json @@ -5,14 +5,22 @@ "author": "Paima Studios", "license": "MIT", "type": "module", - "exports": { - ".": "./build/index.js" - }, "files": [ "build" ], + "main": "./build/index.js", + "types": "./build/index.d.ts", + "exports": { + ".": { + "default": "./build/index.js", + "types": "./build/index.d.ts" + } + }, "scripts": { - "build": "tsc" + "build": "tsc", + "clean": "rm -r tsconfig.tsbuildinfo build/", + "prepare": "npm run build", + "prepack": "npm run clean" }, "keywords": [ "mina", From f17243f325c55e7c8be16b0a98d0a3f859f87c2b Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Tue, 9 Jul 2024 09:59:25 -0700 Subject: [PATCH 12/33] Fix path in tsconfig.base.json --- tsconfig.base.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.base.json b/tsconfig.base.json index 4f45116b4..3ff349b3f 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -39,7 +39,7 @@ "@paima/crypto/*": ["./packages/paima-sdk/paima-crypto/*"], "@paima/db/*": ["./packages/node-sdk/paima-db/*"], "@paima/evm-contracts/*": ["./packages/contracts/evm-contracts/*"], - "@paima/mina-contracts/*": ["./packages/contracts/mina-contracts/*"], + "@paima/mina-delegation/*": ["./packages/contracts/mina-delegation/*"], "@paima/executors/*": ["./packages/paima-sdk/paima-executors/*"], "@paima/funnel/*": ["./packages/engine/paima-funnel/*"], "@paima/mw-core/*": ["./packages/paima-sdk/paima-mw-core/*"], From d3121705123b492f7e987be542eec9970fbdb8f3 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Thu, 27 Jun 2024 13:43:03 -0700 Subject: [PATCH 13/33] Let IProvider.signMessage accept Uint8Array --- packages/paima-sdk/paima-crypto/src/algorand.ts | 9 +++++---- packages/paima-sdk/paima-providers/src/IProvider.ts | 2 +- packages/paima-sdk/paima-providers/src/algorand.ts | 2 +- packages/paima-sdk/paima-providers/src/cardano.ts | 13 +++++++++---- .../paima-sdk/paima-providers/src/evm/ethers.ts | 6 ++---- .../paima-sdk/paima-providers/src/evm/injected.ts | 8 ++++---- packages/paima-sdk/paima-providers/src/mina.ts | 10 ++++++++-- packages/paima-sdk/paima-providers/src/polkadot.ts | 8 ++++---- 8 files changed, 34 insertions(+), 24 deletions(-) diff --git a/packages/paima-sdk/paima-crypto/src/algorand.ts b/packages/paima-sdk/paima-crypto/src/algorand.ts index 8c364c80d..e27802bc3 100644 --- a/packages/paima-sdk/paima-crypto/src/algorand.ts +++ b/packages/paima-sdk/paima-crypto/src/algorand.ts @@ -1,6 +1,5 @@ import type { SignedTransaction, Transaction, SuggestedParams } from 'algosdk'; import { doLog, hexStringToUint8Array } from '@paima/utils'; -import web3UtilsPkg from 'web3-utils'; import type { IVerify } from './IVerify.js'; export class AlgorandCrypto implements IVerify { @@ -27,9 +26,11 @@ export class AlgorandCrypto implements IVerify { } }; - buildAlgorandTransaction = async (userAddress: string, message: string): Promise => { - const hexMessage = web3UtilsPkg.utf8ToHex(message).slice(2); - const msgArray = hexStringToUint8Array(hexMessage); + buildAlgorandTransaction = async ( + userAddress: string, + message: string | Uint8Array + ): Promise => { + const msgArray = message instanceof Uint8Array ? message : new TextEncoder().encode(message); const SUGGESTED_PARAMS: SuggestedParams = { fee: 0, firstRound: 10, diff --git a/packages/paima-sdk/paima-providers/src/IProvider.ts b/packages/paima-sdk/paima-providers/src/IProvider.ts index 86bd4d078..9759bfe39 100644 --- a/packages/paima-sdk/paima-providers/src/IProvider.ts +++ b/packages/paima-sdk/paima-providers/src/IProvider.ts @@ -59,6 +59,6 @@ export type AddressAndType = { }; export interface IProvider { getConnection(): ActiveConnection; - signMessage(message: string): Promise; + signMessage(message: string | Uint8Array): Promise; getAddress(): AddressAndType; } diff --git a/packages/paima-sdk/paima-providers/src/algorand.ts b/packages/paima-sdk/paima-providers/src/algorand.ts index 0cd483409..3b680ce0c 100644 --- a/packages/paima-sdk/paima-providers/src/algorand.ts +++ b/packages/paima-sdk/paima-providers/src/algorand.ts @@ -130,7 +130,7 @@ export class AlgorandProvider implements IProvider { address: this.address, }; }; - signMessage = async (message: string): Promise => { + signMessage = async (message: string | Uint8Array): Promise => { const txn = await CryptoManager.Algorand().buildAlgorandTransaction( this.getAddress().address, message diff --git a/packages/paima-sdk/paima-providers/src/cardano.ts b/packages/paima-sdk/paima-providers/src/cardano.ts index f0daf4251..3c18b6b12 100644 --- a/packages/paima-sdk/paima-providers/src/cardano.ts +++ b/packages/paima-sdk/paima-providers/src/cardano.ts @@ -1,5 +1,9 @@ -import { AddressType, hexStringToUint8Array, type UserSignature } from '@paima/utils'; -import { utf8ToHex } from 'web3-utils'; +import { + AddressType, + hexStringToUint8Array, + uint8ArrayToHexString, + type UserSignature, +} from '@paima/utils'; import { optionToActive } from './IProvider.js'; import type { ActiveConnection, @@ -166,8 +170,9 @@ export class CardanoProvider implements IProvider { address: this.address.bech32, }; }; - signMessage = async (message: string): Promise => { - const hexMessage = utf8ToHex(message).slice(2); + signMessage = async (message: string | Uint8Array): Promise => { + const msgArray = message instanceof Uint8Array ? message : new TextEncoder().encode(message); + const hexMessage = uint8ArrayToHexString(msgArray); const address = this.conn.metadata.name === 'nami' ? this.address.hex : this.address.bech32; const { signature, key } = await this.conn.api.signData(address, hexMessage); return `${signature}+${key}`; diff --git a/packages/paima-sdk/paima-providers/src/evm/ethers.ts b/packages/paima-sdk/paima-providers/src/evm/ethers.ts index dbed6beff..3a52286cb 100644 --- a/packages/paima-sdk/paima-providers/src/evm/ethers.ts +++ b/packages/paima-sdk/paima-providers/src/evm/ethers.ts @@ -9,7 +9,6 @@ import type { } from '../IProvider.js'; import { DEFAULT_GAS_LIMIT, type EvmAddress } from './types.js'; import { ProviderNotInitialized } from '../errors.js'; -import { utf8ToHex } from 'web3-utils'; import { AddressType } from '@paima/utils'; export type EthersApi = Signer; @@ -71,9 +70,8 @@ export class EthersEvmProvider implements IProvider { address: this.address.toLowerCase(), }; }; - signMessage = async (message: string): Promise => { - const hexMessage = utf8ToHex(message); - const buffer = Buffer.from(hexMessage.slice(2), 'hex'); + signMessage = async (message: string | Uint8Array): Promise => { + const buffer = message instanceof Uint8Array ? message : new TextEncoder().encode(message); const signature = await this.conn.api.signMessage(buffer); return signature; }; diff --git a/packages/paima-sdk/paima-providers/src/evm/injected.ts b/packages/paima-sdk/paima-providers/src/evm/injected.ts index 1394848e5..aa1e5bb1c 100644 --- a/packages/paima-sdk/paima-providers/src/evm/injected.ts +++ b/packages/paima-sdk/paima-providers/src/evm/injected.ts @@ -9,10 +9,9 @@ import type { AddressAndType, } from '../IProvider.js'; import { optionToActive } from '../IProvider.js'; -import { utf8ToHex } from 'web3-utils'; import { ProviderApiError, ProviderNotInitialized, WalletNotFound } from '../errors.js'; import type { EvmAddress } from './types.js'; -import { AddressType } from '@paima/utils'; +import { AddressType, uint8ArrayToHexString } from '@paima/utils'; import { getWindow } from '../window.js'; type EIP1193Provider = MetaMaskInpageProvider; @@ -269,8 +268,9 @@ export class EvmInjectedProvider implements IProvider { address: this.address.toLowerCase(), }; }; - signMessage = async (message: string): Promise => { - const hexMessage = utf8ToHex(message); + signMessage = async (message: string | Uint8Array): Promise => { + const msgArray = message instanceof Uint8Array ? message : new TextEncoder().encode(message); + const hexMessage = '0x' + uint8ArrayToHexString(msgArray); const signature = await this.conn.api.request({ method: 'personal_sign', params: [hexMessage, this.getAddress().address, ''], diff --git a/packages/paima-sdk/paima-providers/src/mina.ts b/packages/paima-sdk/paima-providers/src/mina.ts index 77d4f4861..9b040f7b8 100644 --- a/packages/paima-sdk/paima-providers/src/mina.ts +++ b/packages/paima-sdk/paima-providers/src/mina.ts @@ -138,10 +138,16 @@ export class MinaProvider implements IProvider { address: this.address, }; }; - signMessage = async (message: string): Promise => { + signMessage = async (message: string | Uint8Array): Promise => { + // Seems like the aurowallet package requires strings because it uses JSON, + // so throw if the Uint8Array isn't valid UTF-8. + const strMessage = + message instanceof Uint8Array + ? new TextDecoder('utf-8', { fatal: true }).decode(message) + : message; // There is no way of choosing the signing account here. At most we could // monitor the changed events and erroring out if it changed. - const signed = await this.conn.api.signMessage({ message: message }); + const signed = await this.conn.api.signMessage({ message: strMessage }); if ('signature' in signed) { const { signature } = signed; diff --git a/packages/paima-sdk/paima-providers/src/polkadot.ts b/packages/paima-sdk/paima-providers/src/polkadot.ts index 6ab58a0eb..97e513e96 100644 --- a/packages/paima-sdk/paima-providers/src/polkadot.ts +++ b/packages/paima-sdk/paima-providers/src/polkadot.ts @@ -1,4 +1,4 @@ -import { AddressType } from '@paima/utils'; +import { AddressType, uint8ArrayToHexString } from '@paima/utils'; import type { ActiveConnection, AddressAndType, @@ -12,7 +12,6 @@ import type { import { optionToActive } from './IProvider.js'; import { ProviderApiError, ProviderNotInitialized, WalletNotFound } from './errors.js'; import type { InjectedExtension, InjectedWindowProvider } from '@polkadot/extension-inject/types'; -import { utf8ToHex } from 'web3-utils'; import { getWindow } from './window.js'; export type PolkadotAddress = string; @@ -143,13 +142,14 @@ export class PolkadotProvider implements IProvider { address: this.address, }; }; - signMessage = async (message: string): Promise => { + signMessage = async (message: string | Uint8Array): Promise => { if (this.conn.api.signer.signRaw == null) { throw new ProviderApiError( `[polkadot] extension ${this.conn.metadata.name} does not support signRaw` ); } - const hexMessage = utf8ToHex(message); + const msgArray = message instanceof Uint8Array ? message : new TextEncoder().encode(message); + const hexMessage = '0x' + uint8ArrayToHexString(msgArray); const { signature } = await this.conn.api.signer.signRaw({ address: this.getAddress().address, data: hexMessage, From 29c35fff0dbd2a7dc566fc0c16bd7d3fd2b063a6 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Tue, 9 Jul 2024 13:32:06 -0700 Subject: [PATCH 14/33] Make DelegationOrder.bytesToSign static so it can be used w/o an Eth key --- packages/contracts/mina-delegation/src/delegate.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/contracts/mina-delegation/src/delegate.ts b/packages/contracts/mina-delegation/src/delegate.ts index 3e0d48ea2..aa537dcc9 100644 --- a/packages/contracts/mina-delegation/src/delegate.ts +++ b/packages/contracts/mina-delegation/src/delegate.ts @@ -49,18 +49,18 @@ export class DelegationOrder extends Struct({ /** Ethereum public key that signed the delegation order. */ signer: Secp256k1.provable, }) { - private _innerMessage(): Bytes { - return Bytes.from([...delegationPrefix.bytes, ...encodeKey(this.target)]); + private static _innerMessage(target: PublicKey): Bytes { + return Bytes.from([...delegationPrefix.bytes, ...encodeKey(target)]); } /** Get the message for an Etherum wallet to sign, WITHOUT the Ethereum prefix. */ - bytesToSign(): Uint8Array { - return this._innerMessage().toBytes(); + static bytesToSign(target: PublicKey): Uint8Array { + return this._innerMessage(target).toBytes(); } /** Validate that the given Ethereum signature matches this order, WITH the Ethereum prefix. */ assertSignatureMatches(signature: Ecdsa) { - const inner = this._innerMessage(); + const inner = DelegationOrder._innerMessage(this.target); const fullMessage = Bytes.from([ ...ethereumPrefix.bytes, ...Bytes.fromString(String(inner.length)).bytes, From 25517afafe66c075125deb74408f015ed7887054 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Tue, 9 Jul 2024 15:33:21 -0700 Subject: [PATCH 15/33] Update to o1js 1.4.0 --- package-lock.json | 3 ++- packages/contracts/mina-delegation/package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8bc136b41..8d1e4a60f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35436,10 +35436,11 @@ } }, "packages/contracts/mina-delegation": { + "name": "@paima/mina-delegation", "version": "3.1.0", "license": "MIT", "dependencies": { - "o1js": "^1.3.1" + "o1js": "^1.4.0" }, "devDependencies": { "eslint-plugin-o1js": "^0.4.0", diff --git a/packages/contracts/mina-delegation/package.json b/packages/contracts/mina-delegation/package.json index cd8d1f9dc..43d83e7ba 100644 --- a/packages/contracts/mina-delegation/package.json +++ b/packages/contracts/mina-delegation/package.json @@ -28,7 +28,7 @@ "o1js" ], "dependencies": { - "o1js": "^1.3.1" + "o1js": "^1.4.0" }, "devDependencies": { "eslint-plugin-o1js": "^0.4.0", From ce3ef33e2eea4232bc27225c14ce7f95e92f79a4 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Tue, 9 Jul 2024 15:33:36 -0700 Subject: [PATCH 16/33] Accept bare 0x public keys in Secp256k1.fromHex --- .../contracts/mina-delegation/src/delegate.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/contracts/mina-delegation/src/delegate.ts b/packages/contracts/mina-delegation/src/delegate.ts index aa537dcc9..de1ea280d 100644 --- a/packages/contracts/mina-delegation/src/delegate.ts +++ b/packages/contracts/mina-delegation/src/delegate.ts @@ -16,15 +16,21 @@ import { /** A Mina foreign curve for Secp256k1, like Ethereum uses. */ export class Secp256k1 extends createForeignCurve(Crypto.CurveParams.Secp256k1) { - /** Convert a standard 0x04{128 hex digits} public key into this provable struct. */ + /** Convert a standard hex public key into this provable struct. */ static fromHex(publicKey: `0x${string}`): Secp256k1 { - if (!publicKey.startsWith('0x04') || publicKey.length != 4 + 64 + 64) { + if (publicKey.startsWith('0x04') && publicKey.length === 4 + 64 + 64) { + return Secp256k1.from({ + x: BigInt('0x' + publicKey.substring(4, 4 + 64)), + y: BigInt('0x' + publicKey.substring(4 + 64, 4 + 64 + 64)), + }); + } else if (publicKey.startsWith('0x') && publicKey.length === 2 + 64 + 64) { + return Secp256k1.from({ + x: BigInt('0x' + publicKey.substring(2, 2 + 64)), + y: BigInt('0x' + publicKey.substring(2 + 64, 2 + 64 + 64)), + }); + } else { throw new Error('Bad public key format'); } - return Secp256k1.from({ - x: BigInt('0x' + publicKey.substring(4, 4 + 64)), - y: BigInt('0x' + publicKey.substring(4 + 64, 4 + 64 + 64)), - }); } } From bd77e20dcf3859d7cfe94aa7aaf5ca57e8055298 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Tue, 9 Jul 2024 16:33:56 -0700 Subject: [PATCH 17/33] Re-export o1js PublicKey to solve instanceof woes --- packages/contracts/mina-delegation/src/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/contracts/mina-delegation/src/index.ts b/packages/contracts/mina-delegation/src/index.ts index 4e44ce4bd..2f9659872 100644 --- a/packages/contracts/mina-delegation/src/index.ts +++ b/packages/contracts/mina-delegation/src/index.ts @@ -1,3 +1,7 @@ +// Re-export o1js types that are part of our public interface, or else stuff +// might fail `instanceof` checks and cause problems if o1js is double-bundled. +export { PublicKey } from 'o1js'; +// Export our actual contracts. export { DelegationOrder, DelegationOrderProgram, From 426c6155c73d31301ef6882c38d92462f43abcfe Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Mon, 15 Jul 2024 13:42:20 -0700 Subject: [PATCH 18/33] Use o1js's v2 foreignCurve and ecdsa support --- packages/contracts/mina-delegation/package.json | 2 +- packages/contracts/mina-delegation/src/delegate.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/contracts/mina-delegation/package.json b/packages/contracts/mina-delegation/package.json index 43d83e7ba..c4480074f 100644 --- a/packages/contracts/mina-delegation/package.json +++ b/packages/contracts/mina-delegation/package.json @@ -28,7 +28,7 @@ "o1js" ], "dependencies": { - "o1js": "^1.4.0" + "o1js": "^1.5.0" }, "devDependencies": { "eslint-plugin-o1js": "^0.4.0", diff --git a/packages/contracts/mina-delegation/src/delegate.ts b/packages/contracts/mina-delegation/src/delegate.ts index de1ea280d..96006b2cf 100644 --- a/packages/contracts/mina-delegation/src/delegate.ts +++ b/packages/contracts/mina-delegation/src/delegate.ts @@ -7,15 +7,15 @@ import { Struct, UInt8, ZkProgram, - createEcdsa, - createForeignCurve, + createEcdsaV2, + createForeignCurveV2, } from 'o1js'; // ---------------------------------------------------------------------------- // Common data types /** A Mina foreign curve for Secp256k1, like Ethereum uses. */ -export class Secp256k1 extends createForeignCurve(Crypto.CurveParams.Secp256k1) { +export class Secp256k1 extends createForeignCurveV2(Crypto.CurveParams.Secp256k1) { /** Convert a standard hex public key into this provable struct. */ static fromHex(publicKey: `0x${string}`): Secp256k1 { if (publicKey.startsWith('0x04') && publicKey.length === 4 + 64 + 64) { @@ -35,7 +35,7 @@ export class Secp256k1 extends createForeignCurve(Crypto.CurveParams.Secp256k1) } /** A Mina-provable ECDSA signature on the Secp256k1 curve, like Ethereum uses. */ -export class Ecdsa extends createEcdsa(Secp256k1) { +export class Ecdsa extends createEcdsaV2(Secp256k1) { // o1js-provided fromHex is good enough } From ff52eb218ef1a9e2e2afead2e3adc9485eb3c9b5 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Mon, 15 Jul 2024 16:54:02 -0700 Subject: [PATCH 19/33] Revert "Use o1js's v2 foreignCurve and ecdsa support" This reverts commit 426c6155c73d31301ef6882c38d92462f43abcfe. --- packages/contracts/mina-delegation/package.json | 2 +- packages/contracts/mina-delegation/src/delegate.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/contracts/mina-delegation/package.json b/packages/contracts/mina-delegation/package.json index c4480074f..43d83e7ba 100644 --- a/packages/contracts/mina-delegation/package.json +++ b/packages/contracts/mina-delegation/package.json @@ -28,7 +28,7 @@ "o1js" ], "dependencies": { - "o1js": "^1.5.0" + "o1js": "^1.4.0" }, "devDependencies": { "eslint-plugin-o1js": "^0.4.0", diff --git a/packages/contracts/mina-delegation/src/delegate.ts b/packages/contracts/mina-delegation/src/delegate.ts index 96006b2cf..de1ea280d 100644 --- a/packages/contracts/mina-delegation/src/delegate.ts +++ b/packages/contracts/mina-delegation/src/delegate.ts @@ -7,15 +7,15 @@ import { Struct, UInt8, ZkProgram, - createEcdsaV2, - createForeignCurveV2, + createEcdsa, + createForeignCurve, } from 'o1js'; // ---------------------------------------------------------------------------- // Common data types /** A Mina foreign curve for Secp256k1, like Ethereum uses. */ -export class Secp256k1 extends createForeignCurveV2(Crypto.CurveParams.Secp256k1) { +export class Secp256k1 extends createForeignCurve(Crypto.CurveParams.Secp256k1) { /** Convert a standard hex public key into this provable struct. */ static fromHex(publicKey: `0x${string}`): Secp256k1 { if (publicKey.startsWith('0x04') && publicKey.length === 4 + 64 + 64) { @@ -35,7 +35,7 @@ export class Secp256k1 extends createForeignCurveV2(Crypto.CurveParams.Secp256k1 } /** A Mina-provable ECDSA signature on the Secp256k1 curve, like Ethereum uses. */ -export class Ecdsa extends createEcdsaV2(Secp256k1) { +export class Ecdsa extends createEcdsa(Secp256k1) { // o1js-provided fromHex is good enough } From 98e20f85ceff4b06e1d1389c8863b3d7b4e1f7d6 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Mon, 15 Jul 2024 17:29:37 -0700 Subject: [PATCH 20/33] Add @aiken-lang/aiken as a dev dependency --- package-lock.json | 800 +++++++++++++++++- .../inverse-whirlpool/package.json | 4 +- 2 files changed, 802 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8d1e4a60f..afbd00ec9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,6 +59,801 @@ "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==" }, + "node_modules/@aiken-lang/aiken": { + "version": "1.0.29-alpha", + "resolved": "https://registry.npmjs.org/@aiken-lang/aiken/-/aiken-1.0.29-alpha.tgz", + "integrity": "sha512-sA1oe7YPiHkm12kDnJjIychb3OzuaqZWlwnwvnDvRksmm3MmArx7+fTwCSIafeVs1DPD0aSWku+UQj0SYdp34g==", + "dev": true, + "hasInstallScript": true, + "hasShrinkwrap": true, + "license": "Apache-2.0", + "dependencies": { + "axios": "^1.6.8", + "axios-proxy-builder": "^0.1.2", + "console.table": "^0.10.0", + "detect-libc": "^2.0.3", + "rimraf": "^5.0.5", + "tar": "^7.0.1" + }, + "bin": { + "aiken": "run-aiken.js" + }, + "engines": { + "node": ">=14", + "npm": ">=6" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/@aiken-lang/aiken/node_modules/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/axios-proxy-builder": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/axios-proxy-builder/-/axios-proxy-builder-0.1.2.tgz", + "integrity": "sha512-6uBVsBZzkB3tCC8iyx59mCjQckhB8+GQrI9Cop8eC7ybIsvs/KtnNgEBfRMSEa7GqK2VBGUzgjNYMdPIfotyPA==", + "dev": true, + "dependencies": { + "tunnel": "^0.0.6" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/@aiken-lang/aiken/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@aiken-lang/aiken/node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/console.table": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz", + "integrity": "sha512-dPyZofqggxuvSf7WXvNjuRfnsOk1YazkVP8FdxH4tcH2c37wc79/Yl6Bhr7Lsu00KMgy2ql/qCMuNu8xctZM8g==", + "dev": true, + "dependencies": { + "easy-table": "1.1.0" + }, + "engines": { + "node": "> 0.10" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "optional": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/@aiken-lang/aiken/node_modules/easy-table": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz", + "integrity": "sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==", + "dev": true, + "optionalDependencies": { + "wcwidth": ">=1.0.1" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@aiken-lang/aiken/node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/@aiken-lang/aiken/node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/glob": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/@aiken-lang/aiken/node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/minizlib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.1.tgz", + "integrity": "sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==", + "dev": true, + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/path-scurry": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "extraneous": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/@aiken-lang/aiken/node_modules/rimraf": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", + "dev": true, + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/@aiken-lang/aiken/node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/tar": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.0.1.tgz", + "integrity": "sha512-IjMhdQMZFpKsHEQT3woZVxBtCQY+0wk3CVxdRkGXEgyGa0dNS/ehPvOMr2nmfC7x5Zj2N+l6yZUpmICjLGS35w==", + "dev": true, + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^5.0.0", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "optional": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/@aiken-lang/aiken/node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aiken-lang/aiken/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -35396,7 +36191,10 @@ "packages/cardano-contracts/inverse-whirlpool": { "name": "@paima/inverse-whirlpool", "version": "1.0.0", - "license": "MIT" + "license": "MIT", + "devDependencies": { + "@aiken-lang/aiken": "1.0.29-alpha" + } }, "packages/contracts/cardano-contracts": { "name": "@paima/cardano-contracts", diff --git a/packages/cardano-contracts/inverse-whirlpool/package.json b/packages/cardano-contracts/inverse-whirlpool/package.json index 74724dafe..08b3d6e95 100644 --- a/packages/cardano-contracts/inverse-whirlpool/package.json +++ b/packages/cardano-contracts/inverse-whirlpool/package.json @@ -8,6 +8,8 @@ }, "author": "Paima Studios", "license": "MIT", - "dependencies": { + "dependencies": {}, + "devDependencies": { + "@aiken-lang/aiken": "1.0.29-alpha" } } From 07ba92ab345c89253c20a622c24d7cc55bdfe020 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Tue, 16 Jul 2024 10:02:34 -0700 Subject: [PATCH 21/33] Downgrade o1js to 1.5.0 in lockfile --- package-lock.json | 52 +++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index afbd00ec9..60cac0129 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26370,32 +26370,6 @@ "node": "*" } }, - "node_modules/o1js": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/o1js/-/o1js-1.5.0.tgz", - "integrity": "sha512-VZGEjky4w+srX2inh1uzoviFY8Bb0vez27N6Z9MmtYv317jf8FTCcoRXnmCJInmeVZOWnl60NJ2Jyyk/p4gtJA==", - "license": "Apache-2.0", - "dependencies": { - "blakejs": "1.2.1", - "cachedir": "^2.4.0", - "isomorphic-fetch": "^3.0.0", - "js-sha256": "^0.9.0", - "reflect-metadata": "^0.1.13", - "tslib": "^2.3.0" - }, - "bin": { - "snarky-run": "src/build/run.js" - }, - "engines": { - "node": ">=18.14.0" - } - }, - "node_modules/o1js/node_modules/reflect-metadata": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", - "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", - "license": "Apache-2.0" - }, "node_modules/oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -36245,6 +36219,32 @@ "typescript": "^5.3.3" } }, + "packages/contracts/mina-delegation/node_modules/o1js": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/o1js/-/o1js-1.4.0.tgz", + "integrity": "sha512-n4W1tukEF1ztfsKOoa29PIc9zY6SBMhQI6lXQL+pZ86/0XO3VqE9Gc8GOlxv+zV9+BJWM2c1N25bob72Kt/+EQ==", + "license": "Apache-2.0", + "dependencies": { + "blakejs": "1.2.1", + "cachedir": "^2.4.0", + "isomorphic-fetch": "^3.0.0", + "js-sha256": "^0.9.0", + "reflect-metadata": "^0.1.13", + "tslib": "^2.3.0" + }, + "bin": { + "snarky-run": "src/build/run.js" + }, + "engines": { + "node": ">=18.14.0" + } + }, + "packages/contracts/mina-delegation/node_modules/reflect-metadata": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", + "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", + "license": "Apache-2.0" + }, "packages/engine/paima-funnel": { "name": "@paima/funnel", "version": "1.0.0", From bdaa2132ba984d53d1d380f6e0ecbf7fbf09bd6e Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Tue, 16 Jul 2024 10:03:37 -0700 Subject: [PATCH 22/33] Use base64 in DelegationOrder to avoid binary mess in wallets --- packages/contracts/mina-delegation/src/delegate.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/contracts/mina-delegation/src/delegate.ts b/packages/contracts/mina-delegation/src/delegate.ts index de1ea280d..be27d4ac6 100644 --- a/packages/contracts/mina-delegation/src/delegate.ts +++ b/packages/contracts/mina-delegation/src/delegate.ts @@ -56,7 +56,10 @@ export class DelegationOrder extends Struct({ signer: Secp256k1.provable, }) { private static _innerMessage(target: PublicKey): Bytes { - return Bytes.from([...delegationPrefix.bytes, ...encodeKey(target)]); + return Bytes.from([ + ...delegationPrefix.bytes, + ...Bytes.from(encodeKey(target)).base64Encode().bytes, + ]); } /** Get the message for an Etherum wallet to sign, WITHOUT the Ethereum prefix. */ @@ -69,6 +72,7 @@ export class DelegationOrder extends Struct({ const inner = DelegationOrder._innerMessage(this.target); const fullMessage = Bytes.from([ ...ethereumPrefix.bytes, + // NOTE: `inner.length` is effectively a constant so it's okay to bake it in. ...Bytes.fromString(String(inner.length)).bytes, ...inner.bytes, ]); From 3f898f6a4901decaf22c0d78f336ca5cfbcccb9d Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Tue, 16 Jul 2024 17:02:05 -0700 Subject: [PATCH 23/33] Tweak bytesToSign API --- packages/contracts/mina-delegation/src/delegate.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/contracts/mina-delegation/src/delegate.ts b/packages/contracts/mina-delegation/src/delegate.ts index be27d4ac6..b4ea52174 100644 --- a/packages/contracts/mina-delegation/src/delegate.ts +++ b/packages/contracts/mina-delegation/src/delegate.ts @@ -62,8 +62,12 @@ export class DelegationOrder extends Struct({ ]); } - /** Get the message for an Etherum wallet to sign, WITHOUT the Ethereum prefix. */ - static bytesToSign(target: PublicKey): Uint8Array { + /** + * Get the message for an Etherum wallet to sign, WITHOUT the Ethereum prefix. + * This is printable and should be passed to something like `personal_sign`. + */ + static bytesToSign({ target }: { target: PublicKey }): Uint8Array { + // Accepts an object so you can pass just a PublicKey OR a DelegationOrder. return this._innerMessage(target).toBytes(); } From ba90ddf93038a97b41eccfbdb809981993fbede7 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Wed, 17 Jul 2024 11:54:11 -0700 Subject: [PATCH 24/33] Proactively fix up PublicKeys with wrong instanceof --- packages/contracts/mina-delegation/src/delegate.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/contracts/mina-delegation/src/delegate.ts b/packages/contracts/mina-delegation/src/delegate.ts index b4ea52174..d2a32d49c 100644 --- a/packages/contracts/mina-delegation/src/delegate.ts +++ b/packages/contracts/mina-delegation/src/delegate.ts @@ -55,6 +55,16 @@ export class DelegationOrder extends Struct({ /** Ethereum public key that signed the delegation order. */ signer: Secp256k1.provable, }) { + constructor(value: { target: PublicKey; signer: Secp256k1 }) { + if (!(value.target instanceof PublicKey)) { + // Compensate for the possibility of duplicate o1js libraries that aren't + // `instanceof` each other, which messes up checks inside o1js. Can be + // caused by `npm link`ing to this package, for example. + value = { ...value, target: PublicKey.fromBase58((value.target as PublicKey).toBase58()) }; + } + super(value); + } + private static _innerMessage(target: PublicKey): Bytes { return Bytes.from([ ...delegationPrefix.bytes, From 9226f89d5696f3f7fd6b3bc196c8f4b424aacda1 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Fri, 19 Jul 2024 16:11:56 -0700 Subject: [PATCH 25/33] Delete unused DelegationOrder.hash function --- packages/contracts/mina-delegation/src/delegate.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/contracts/mina-delegation/src/delegate.ts b/packages/contracts/mina-delegation/src/delegate.ts index d2a32d49c..aa32f2285 100644 --- a/packages/contracts/mina-delegation/src/delegate.ts +++ b/packages/contracts/mina-delegation/src/delegate.ts @@ -2,7 +2,6 @@ import { Bool, Bytes, Crypto, - Poseidon, PublicKey, Struct, UInt8, @@ -92,15 +91,6 @@ export class DelegationOrder extends Struct({ ]); signature.verifyV2(fullMessage, this.signer).assertTrue(); } - - /** Hash this entire order for use as a MerkleMap key. */ - hash() { - return Poseidon.hashWithPrefix('DelegationOrder', [ - ...this.target.toFields(), - ...this.signer.x.toFields(), - ...this.signer.y.toFields(), - ]); - } } function encodeKey(k: PublicKey): UInt8[] { From fa53b5827e069bd8d2b1260a8a007cd140005a6d Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Fri, 19 Jul 2024 16:16:14 -0700 Subject: [PATCH 26/33] Make DelegationOrder prefix configurable --- .../contracts/mina-delegation/src/delegate.ts | 166 ++++++++++-------- .../contracts/mina-delegation/src/index.ts | 12 +- 2 files changed, 96 insertions(+), 82 deletions(-) diff --git a/packages/contracts/mina-delegation/src/delegate.ts b/packages/contracts/mina-delegation/src/delegate.ts index aa32f2285..8f984f63f 100644 --- a/packages/contracts/mina-delegation/src/delegate.ts +++ b/packages/contracts/mina-delegation/src/delegate.ts @@ -38,61 +38,10 @@ export class Ecdsa extends createEcdsa(Secp256k1) { // o1js-provided fromHex is good enough } -// Ethereum's fixed prefix. +/** Ethereum's fixed prefix for `personal_sign` messages. **/ const ethereumPrefix = Bytes.fromString('\x19Ethereum Signed Message:\n'); -// A prefix to distinguish this delegation order scheme from what might be -// similar-looking messages. -const delegationPrefix = Bytes.fromString('DELEGATE-WALLET:'); - -/** - * An order that a particular EVM address has signed to authorize (delegate) - * a Mina address to act on its behalf. - */ -export class DelegationOrder extends Struct({ - /** Mina public key that the delegation order is issued for. */ - target: PublicKey, - /** Ethereum public key that signed the delegation order. */ - signer: Secp256k1.provable, -}) { - constructor(value: { target: PublicKey; signer: Secp256k1 }) { - if (!(value.target instanceof PublicKey)) { - // Compensate for the possibility of duplicate o1js libraries that aren't - // `instanceof` each other, which messes up checks inside o1js. Can be - // caused by `npm link`ing to this package, for example. - value = { ...value, target: PublicKey.fromBase58((value.target as PublicKey).toBase58()) }; - } - super(value); - } - - private static _innerMessage(target: PublicKey): Bytes { - return Bytes.from([ - ...delegationPrefix.bytes, - ...Bytes.from(encodeKey(target)).base64Encode().bytes, - ]); - } - - /** - * Get the message for an Etherum wallet to sign, WITHOUT the Ethereum prefix. - * This is printable and should be passed to something like `personal_sign`. - */ - static bytesToSign({ target }: { target: PublicKey }): Uint8Array { - // Accepts an object so you can pass just a PublicKey OR a DelegationOrder. - return this._innerMessage(target).toBytes(); - } - - /** Validate that the given Ethereum signature matches this order, WITH the Ethereum prefix. */ - assertSignatureMatches(signature: Ecdsa) { - const inner = DelegationOrder._innerMessage(this.target); - const fullMessage = Bytes.from([ - ...ethereumPrefix.bytes, - // NOTE: `inner.length` is effectively a constant so it's okay to bake it in. - ...Bytes.fromString(String(inner.length)).bytes, - ...inner.bytes, - ]); - signature.verifyV2(fullMessage, this.signer).assertTrue(); - } -} +/** Pack 254 bits of key's X and 1 bit of isOdd into 32 bytes. */ function encodeKey(k: PublicKey): UInt8[] { const bytes = []; const bits = [...k.x.toBits(254), k.isOdd]; @@ -110,28 +59,103 @@ function boolToU8(bool: Bool): UInt8 { return UInt8.from(bool.toField()); } -// ---------------------------------------------------------------------------- -// The provable program itself - /** - * A simple {@link ZkProgram} that proves that a valid signature exists for an - * input {@link DelegationOrder}. + * Prepare a ZkProgram that verifies an EVM signature delegating to a Mina + * address under a specific prefix. The prefix will be seen by the user when + * signing the message and should clearly indicate the limited scope of the + * validity of the signature. + * + * @param prefix A prefix to distinguish these delegation orders from those for other systems. + * @returns An o1js Struct, ZkProgram, and Proof tied to that prefix. + * + * @example + * const { DelegationOrder, DelegationOrderProgram, DelegationOrderProof } = + * delegateEvmToMina('Click & Moo login: '); */ -export const DelegationOrderProgram = ZkProgram({ - name: 'DelegationOrderProgram', +export function delegateEvmToMina(prefix: string) { + // ---------------------------------------------------------------------------- + // Per-prefix data types + const delegationPrefix = Bytes.fromString(prefix); + + class DelegationOrder extends Struct({ + /** Mina public key that the delegation order is issued for. */ + target: PublicKey, + /** Ethereum public key that signed the delegation order. */ + signer: Secp256k1.provable, + }) { + constructor(value: { target: PublicKey; signer: Secp256k1 }) { + if (!(value.target instanceof PublicKey)) { + // Compensate for the possibility of duplicate o1js libraries that aren't + // `instanceof` each other, which messes up checks inside o1js. Can be + // caused by `npm link`ing to this package, for example. + value = { ...value, target: PublicKey.fromBase58((value.target as PublicKey).toBase58()) }; + } + super(value); + } - publicInput: DelegationOrder, + static #_innerMessage(target: PublicKey): Bytes { + return Bytes.from([ + ...delegationPrefix.bytes, + // Base64-encode encodeKey() + ...Bytes.from(encodeKey(target)).base64Encode().bytes, + ]); + } - methods: { - sign: { - privateInputs: [Ecdsa.provable], + /** + * Get the message for an Etherum wallet to sign, WITHOUT the Ethereum prefix. + * This is printable and should be passed to something like `personal_sign`. + */ + static bytesToSign({ target }: { target: PublicKey }): Uint8Array { + // Accepts an object so you can pass just a PublicKey OR a DelegationOrder. + return this.#_innerMessage(target).toBytes(); + } + + /** Validate that the given Ethereum signature matches this order, WITH the Ethereum prefix. */ + assertSignatureMatches(signature: Ecdsa) { + const inner = DelegationOrder.#_innerMessage(this.target); + const fullMessage = Bytes.from([ + ...ethereumPrefix.bytes, + // NOTE: `inner.length` is effectively a constant so it's okay to bake it in. + ...Bytes.fromString(String(inner.length)).bytes, + ...inner.bytes, + ]); + signature.verifyV2(fullMessage, this.signer).assertTrue(); + } + } - async method(order: DelegationOrder, signature: Ecdsa) { - order.assertSignatureMatches(signature); + // ---------------------------------------------------------------------------- + // The provable program itself + + const DelegationOrderProgram = ZkProgram({ + name: `${prefix}DelegationOrderProgram`, + + publicInput: DelegationOrder, + + methods: { + sign: { + privateInputs: [Ecdsa.provable], + + async method(order: DelegationOrder, signature: Ecdsa) { + order.assertSignatureMatches(signature); + }, }, }, - }, -}); - -/** A verifiable proof of {@link DelegationOrderProgram}'s success. */ -export class DelegationOrderProof extends ZkProgram.Proof(DelegationOrderProgram) {} + }); + + class DelegationOrderProof extends ZkProgram.Proof(DelegationOrderProgram) {} + + return { + /** + * An order that a particular EVM address has signed to authorize (delegate) + * a Mina address to act on its behalf. + */ + DelegationOrder, + /** + * A simple {@link ZkProgram} that proves that a valid signature exists for an + * input {@link DelegationOrder}. + */ + DelegationOrderProgram, + /** A verifiable proof of {@link DelegationOrderProgram}'s success. */ + DelegationOrderProof, + }; +} diff --git a/packages/contracts/mina-delegation/src/index.ts b/packages/contracts/mina-delegation/src/index.ts index 2f9659872..86fd3f057 100644 --- a/packages/contracts/mina-delegation/src/index.ts +++ b/packages/contracts/mina-delegation/src/index.ts @@ -1,11 +1 @@ -// Re-export o1js types that are part of our public interface, or else stuff -// might fail `instanceof` checks and cause problems if o1js is double-bundled. -export { PublicKey } from 'o1js'; -// Export our actual contracts. -export { - DelegationOrder, - DelegationOrderProgram, - DelegationOrderProof, - Ecdsa, - Secp256k1, -} from './delegate.js'; +export { delegateEvmToMina, Ecdsa, Secp256k1 } from './delegate.js'; From bc3ec8d60814c7bb21ca8e50061b7cf3ecea0930 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Fri, 19 Jul 2024 16:16:38 -0700 Subject: [PATCH 27/33] Merge delegate.ts into index.ts --- .../contracts/mina-delegation/src/delegate.ts | 161 ----------------- .../contracts/mina-delegation/src/index.ts | 162 +++++++++++++++++- 2 files changed, 161 insertions(+), 162 deletions(-) delete mode 100644 packages/contracts/mina-delegation/src/delegate.ts diff --git a/packages/contracts/mina-delegation/src/delegate.ts b/packages/contracts/mina-delegation/src/delegate.ts deleted file mode 100644 index 8f984f63f..000000000 --- a/packages/contracts/mina-delegation/src/delegate.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { - Bool, - Bytes, - Crypto, - PublicKey, - Struct, - UInt8, - ZkProgram, - createEcdsa, - createForeignCurve, -} from 'o1js'; - -// ---------------------------------------------------------------------------- -// Common data types - -/** A Mina foreign curve for Secp256k1, like Ethereum uses. */ -export class Secp256k1 extends createForeignCurve(Crypto.CurveParams.Secp256k1) { - /** Convert a standard hex public key into this provable struct. */ - static fromHex(publicKey: `0x${string}`): Secp256k1 { - if (publicKey.startsWith('0x04') && publicKey.length === 4 + 64 + 64) { - return Secp256k1.from({ - x: BigInt('0x' + publicKey.substring(4, 4 + 64)), - y: BigInt('0x' + publicKey.substring(4 + 64, 4 + 64 + 64)), - }); - } else if (publicKey.startsWith('0x') && publicKey.length === 2 + 64 + 64) { - return Secp256k1.from({ - x: BigInt('0x' + publicKey.substring(2, 2 + 64)), - y: BigInt('0x' + publicKey.substring(2 + 64, 2 + 64 + 64)), - }); - } else { - throw new Error('Bad public key format'); - } - } -} - -/** A Mina-provable ECDSA signature on the Secp256k1 curve, like Ethereum uses. */ -export class Ecdsa extends createEcdsa(Secp256k1) { - // o1js-provided fromHex is good enough -} - -/** Ethereum's fixed prefix for `personal_sign` messages. **/ -const ethereumPrefix = Bytes.fromString('\x19Ethereum Signed Message:\n'); - -/** Pack 254 bits of key's X and 1 bit of isOdd into 32 bytes. */ -function encodeKey(k: PublicKey): UInt8[] { - const bytes = []; - const bits = [...k.x.toBits(254), k.isOdd]; - for (let i = 0; i < bits.length; i += 8) { - let value = new UInt8(0); - for (let j = 0; j < 8; j++) { - value = value.mul(2).add(boolToU8(bits[i + j] ?? Bool(false))); - } - bytes.push(value); - } - return bytes; -} - -function boolToU8(bool: Bool): UInt8 { - return UInt8.from(bool.toField()); -} - -/** - * Prepare a ZkProgram that verifies an EVM signature delegating to a Mina - * address under a specific prefix. The prefix will be seen by the user when - * signing the message and should clearly indicate the limited scope of the - * validity of the signature. - * - * @param prefix A prefix to distinguish these delegation orders from those for other systems. - * @returns An o1js Struct, ZkProgram, and Proof tied to that prefix. - * - * @example - * const { DelegationOrder, DelegationOrderProgram, DelegationOrderProof } = - * delegateEvmToMina('Click & Moo login: '); - */ -export function delegateEvmToMina(prefix: string) { - // ---------------------------------------------------------------------------- - // Per-prefix data types - const delegationPrefix = Bytes.fromString(prefix); - - class DelegationOrder extends Struct({ - /** Mina public key that the delegation order is issued for. */ - target: PublicKey, - /** Ethereum public key that signed the delegation order. */ - signer: Secp256k1.provable, - }) { - constructor(value: { target: PublicKey; signer: Secp256k1 }) { - if (!(value.target instanceof PublicKey)) { - // Compensate for the possibility of duplicate o1js libraries that aren't - // `instanceof` each other, which messes up checks inside o1js. Can be - // caused by `npm link`ing to this package, for example. - value = { ...value, target: PublicKey.fromBase58((value.target as PublicKey).toBase58()) }; - } - super(value); - } - - static #_innerMessage(target: PublicKey): Bytes { - return Bytes.from([ - ...delegationPrefix.bytes, - // Base64-encode encodeKey() - ...Bytes.from(encodeKey(target)).base64Encode().bytes, - ]); - } - - /** - * Get the message for an Etherum wallet to sign, WITHOUT the Ethereum prefix. - * This is printable and should be passed to something like `personal_sign`. - */ - static bytesToSign({ target }: { target: PublicKey }): Uint8Array { - // Accepts an object so you can pass just a PublicKey OR a DelegationOrder. - return this.#_innerMessage(target).toBytes(); - } - - /** Validate that the given Ethereum signature matches this order, WITH the Ethereum prefix. */ - assertSignatureMatches(signature: Ecdsa) { - const inner = DelegationOrder.#_innerMessage(this.target); - const fullMessage = Bytes.from([ - ...ethereumPrefix.bytes, - // NOTE: `inner.length` is effectively a constant so it's okay to bake it in. - ...Bytes.fromString(String(inner.length)).bytes, - ...inner.bytes, - ]); - signature.verifyV2(fullMessage, this.signer).assertTrue(); - } - } - - // ---------------------------------------------------------------------------- - // The provable program itself - - const DelegationOrderProgram = ZkProgram({ - name: `${prefix}DelegationOrderProgram`, - - publicInput: DelegationOrder, - - methods: { - sign: { - privateInputs: [Ecdsa.provable], - - async method(order: DelegationOrder, signature: Ecdsa) { - order.assertSignatureMatches(signature); - }, - }, - }, - }); - - class DelegationOrderProof extends ZkProgram.Proof(DelegationOrderProgram) {} - - return { - /** - * An order that a particular EVM address has signed to authorize (delegate) - * a Mina address to act on its behalf. - */ - DelegationOrder, - /** - * A simple {@link ZkProgram} that proves that a valid signature exists for an - * input {@link DelegationOrder}. - */ - DelegationOrderProgram, - /** A verifiable proof of {@link DelegationOrderProgram}'s success. */ - DelegationOrderProof, - }; -} diff --git a/packages/contracts/mina-delegation/src/index.ts b/packages/contracts/mina-delegation/src/index.ts index 86fd3f057..8f984f63f 100644 --- a/packages/contracts/mina-delegation/src/index.ts +++ b/packages/contracts/mina-delegation/src/index.ts @@ -1 +1,161 @@ -export { delegateEvmToMina, Ecdsa, Secp256k1 } from './delegate.js'; +import { + Bool, + Bytes, + Crypto, + PublicKey, + Struct, + UInt8, + ZkProgram, + createEcdsa, + createForeignCurve, +} from 'o1js'; + +// ---------------------------------------------------------------------------- +// Common data types + +/** A Mina foreign curve for Secp256k1, like Ethereum uses. */ +export class Secp256k1 extends createForeignCurve(Crypto.CurveParams.Secp256k1) { + /** Convert a standard hex public key into this provable struct. */ + static fromHex(publicKey: `0x${string}`): Secp256k1 { + if (publicKey.startsWith('0x04') && publicKey.length === 4 + 64 + 64) { + return Secp256k1.from({ + x: BigInt('0x' + publicKey.substring(4, 4 + 64)), + y: BigInt('0x' + publicKey.substring(4 + 64, 4 + 64 + 64)), + }); + } else if (publicKey.startsWith('0x') && publicKey.length === 2 + 64 + 64) { + return Secp256k1.from({ + x: BigInt('0x' + publicKey.substring(2, 2 + 64)), + y: BigInt('0x' + publicKey.substring(2 + 64, 2 + 64 + 64)), + }); + } else { + throw new Error('Bad public key format'); + } + } +} + +/** A Mina-provable ECDSA signature on the Secp256k1 curve, like Ethereum uses. */ +export class Ecdsa extends createEcdsa(Secp256k1) { + // o1js-provided fromHex is good enough +} + +/** Ethereum's fixed prefix for `personal_sign` messages. **/ +const ethereumPrefix = Bytes.fromString('\x19Ethereum Signed Message:\n'); + +/** Pack 254 bits of key's X and 1 bit of isOdd into 32 bytes. */ +function encodeKey(k: PublicKey): UInt8[] { + const bytes = []; + const bits = [...k.x.toBits(254), k.isOdd]; + for (let i = 0; i < bits.length; i += 8) { + let value = new UInt8(0); + for (let j = 0; j < 8; j++) { + value = value.mul(2).add(boolToU8(bits[i + j] ?? Bool(false))); + } + bytes.push(value); + } + return bytes; +} + +function boolToU8(bool: Bool): UInt8 { + return UInt8.from(bool.toField()); +} + +/** + * Prepare a ZkProgram that verifies an EVM signature delegating to a Mina + * address under a specific prefix. The prefix will be seen by the user when + * signing the message and should clearly indicate the limited scope of the + * validity of the signature. + * + * @param prefix A prefix to distinguish these delegation orders from those for other systems. + * @returns An o1js Struct, ZkProgram, and Proof tied to that prefix. + * + * @example + * const { DelegationOrder, DelegationOrderProgram, DelegationOrderProof } = + * delegateEvmToMina('Click & Moo login: '); + */ +export function delegateEvmToMina(prefix: string) { + // ---------------------------------------------------------------------------- + // Per-prefix data types + const delegationPrefix = Bytes.fromString(prefix); + + class DelegationOrder extends Struct({ + /** Mina public key that the delegation order is issued for. */ + target: PublicKey, + /** Ethereum public key that signed the delegation order. */ + signer: Secp256k1.provable, + }) { + constructor(value: { target: PublicKey; signer: Secp256k1 }) { + if (!(value.target instanceof PublicKey)) { + // Compensate for the possibility of duplicate o1js libraries that aren't + // `instanceof` each other, which messes up checks inside o1js. Can be + // caused by `npm link`ing to this package, for example. + value = { ...value, target: PublicKey.fromBase58((value.target as PublicKey).toBase58()) }; + } + super(value); + } + + static #_innerMessage(target: PublicKey): Bytes { + return Bytes.from([ + ...delegationPrefix.bytes, + // Base64-encode encodeKey() + ...Bytes.from(encodeKey(target)).base64Encode().bytes, + ]); + } + + /** + * Get the message for an Etherum wallet to sign, WITHOUT the Ethereum prefix. + * This is printable and should be passed to something like `personal_sign`. + */ + static bytesToSign({ target }: { target: PublicKey }): Uint8Array { + // Accepts an object so you can pass just a PublicKey OR a DelegationOrder. + return this.#_innerMessage(target).toBytes(); + } + + /** Validate that the given Ethereum signature matches this order, WITH the Ethereum prefix. */ + assertSignatureMatches(signature: Ecdsa) { + const inner = DelegationOrder.#_innerMessage(this.target); + const fullMessage = Bytes.from([ + ...ethereumPrefix.bytes, + // NOTE: `inner.length` is effectively a constant so it's okay to bake it in. + ...Bytes.fromString(String(inner.length)).bytes, + ...inner.bytes, + ]); + signature.verifyV2(fullMessage, this.signer).assertTrue(); + } + } + + // ---------------------------------------------------------------------------- + // The provable program itself + + const DelegationOrderProgram = ZkProgram({ + name: `${prefix}DelegationOrderProgram`, + + publicInput: DelegationOrder, + + methods: { + sign: { + privateInputs: [Ecdsa.provable], + + async method(order: DelegationOrder, signature: Ecdsa) { + order.assertSignatureMatches(signature); + }, + }, + }, + }); + + class DelegationOrderProof extends ZkProgram.Proof(DelegationOrderProgram) {} + + return { + /** + * An order that a particular EVM address has signed to authorize (delegate) + * a Mina address to act on its behalf. + */ + DelegationOrder, + /** + * A simple {@link ZkProgram} that proves that a valid signature exists for an + * input {@link DelegationOrder}. + */ + DelegationOrderProgram, + /** A verifiable proof of {@link DelegationOrderProgram}'s success. */ + DelegationOrderProof, + }; +} From 5aa0f8cdb1be6c4c7e6eb5233c918f2eb9530aaa Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Wed, 24 Jul 2024 17:32:50 -0700 Subject: [PATCH 28/33] Add DelegationOrderProof type --- .../contracts/mina-delegation/src/index.ts | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/contracts/mina-delegation/src/index.ts b/packages/contracts/mina-delegation/src/index.ts index 8f984f63f..18b0e6e3f 100644 --- a/packages/contracts/mina-delegation/src/index.ts +++ b/packages/contracts/mina-delegation/src/index.ts @@ -2,9 +2,13 @@ import { Bool, Bytes, Crypto, + DynamicProof, + ForeignCurve, + Proof, PublicKey, Struct, UInt8, + Void, ZkProgram, createEcdsa, createForeignCurve, @@ -59,6 +63,18 @@ function boolToU8(bool: Bool): UInt8 { return UInt8.from(bool.toField()); } +export type DelegationOrderProof = Proof< + { + // real data + target: PublicKey; + signer: ForeignCurve; + // sort of a type system marker that the interior is a DelegationOrder? + assertSignatureMatches(signature: Ecdsa): void; + }, + void +>; +type _DelegationOrderProof = DelegationOrderProof; + /** * Prepare a ZkProgram that verifies an EVM signature delegating to a Mina * address under a specific prefix. The prefix will be seen by the user when @@ -142,7 +158,15 @@ export function delegateEvmToMina(prefix: string) { }, }); - class DelegationOrderProof extends ZkProgram.Proof(DelegationOrderProgram) {} + class DelegationOrderProof + extends ZkProgram.Proof(DelegationOrderProgram) + implements _DelegationOrderProof {} + + class DynamicDelegationOrderProof extends DynamicProof { + static override publicInputType = DelegationOrder; + static override publicOutputType = Void; + static override maxProofsVerified = 0 as const; + } return { /** @@ -157,5 +181,7 @@ export function delegateEvmToMina(prefix: string) { DelegationOrderProgram, /** A verifiable proof of {@link DelegationOrderProgram}'s success. */ DelegationOrderProof, + // /** A dynamic version of {@link DelegationOrderProof}. */ + // DynamicDelegationOrderProof, <- causes ts4904 until we explicitly declare the return type }; } From 6715d77af68d392d8b7617c04178f6099daba0f6 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Thu, 25 Jul 2024 14:25:14 -0700 Subject: [PATCH 29/33] Simplify calling Secp256k1.fromHex by accepting string --- packages/contracts/mina-delegation/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/mina-delegation/src/index.ts b/packages/contracts/mina-delegation/src/index.ts index 18b0e6e3f..68c695314 100644 --- a/packages/contracts/mina-delegation/src/index.ts +++ b/packages/contracts/mina-delegation/src/index.ts @@ -20,7 +20,7 @@ import { /** A Mina foreign curve for Secp256k1, like Ethereum uses. */ export class Secp256k1 extends createForeignCurve(Crypto.CurveParams.Secp256k1) { /** Convert a standard hex public key into this provable struct. */ - static fromHex(publicKey: `0x${string}`): Secp256k1 { + static fromHex(publicKey: string): Secp256k1 { if (publicKey.startsWith('0x04') && publicKey.length === 4 + 64 + 64) { return Secp256k1.from({ x: BigInt('0x' + publicKey.substring(4, 4 + 64)), From 50affe7106ba743fcf7db63f232c032839afa35b Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Fri, 26 Jul 2024 16:01:58 -0700 Subject: [PATCH 30/33] s/DelegationOrder/DelegationCommand/ --- .../contracts/mina-delegation/src/index.ts | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/contracts/mina-delegation/src/index.ts b/packages/contracts/mina-delegation/src/index.ts index 68c695314..fab212cc9 100644 --- a/packages/contracts/mina-delegation/src/index.ts +++ b/packages/contracts/mina-delegation/src/index.ts @@ -63,17 +63,17 @@ function boolToU8(bool: Bool): UInt8 { return UInt8.from(bool.toField()); } -export type DelegationOrderProof = Proof< +export type DelegationCommandProof = Proof< { // real data target: PublicKey; signer: ForeignCurve; - // sort of a type system marker that the interior is a DelegationOrder? + // sort of a type system marker that the interior is a DelegationCommand? assertSignatureMatches(signature: Ecdsa): void; }, void >; -type _DelegationOrderProof = DelegationOrderProof; +type _DelegationCommandProof = DelegationCommandProof; /** * Prepare a ZkProgram that verifies an EVM signature delegating to a Mina @@ -85,7 +85,7 @@ type _DelegationOrderProof = DelegationOrderProof; * @returns An o1js Struct, ZkProgram, and Proof tied to that prefix. * * @example - * const { DelegationOrder, DelegationOrderProgram, DelegationOrderProof } = + * const { DelegationCommand, DelegationCommandProgram, DelegationCommandProof } = * delegateEvmToMina('Click & Moo login: '); */ export function delegateEvmToMina(prefix: string) { @@ -93,7 +93,7 @@ export function delegateEvmToMina(prefix: string) { // Per-prefix data types const delegationPrefix = Bytes.fromString(prefix); - class DelegationOrder extends Struct({ + class DelegationCommand extends Struct({ /** Mina public key that the delegation order is issued for. */ target: PublicKey, /** Ethereum public key that signed the delegation order. */ @@ -122,13 +122,13 @@ export function delegateEvmToMina(prefix: string) { * This is printable and should be passed to something like `personal_sign`. */ static bytesToSign({ target }: { target: PublicKey }): Uint8Array { - // Accepts an object so you can pass just a PublicKey OR a DelegationOrder. + // Accepts an object so you can pass just a PublicKey OR a DelegationCommand. return this.#_innerMessage(target).toBytes(); } /** Validate that the given Ethereum signature matches this order, WITH the Ethereum prefix. */ assertSignatureMatches(signature: Ecdsa) { - const inner = DelegationOrder.#_innerMessage(this.target); + const inner = DelegationCommand.#_innerMessage(this.target); const fullMessage = Bytes.from([ ...ethereumPrefix.bytes, // NOTE: `inner.length` is effectively a constant so it's okay to bake it in. @@ -142,28 +142,28 @@ export function delegateEvmToMina(prefix: string) { // ---------------------------------------------------------------------------- // The provable program itself - const DelegationOrderProgram = ZkProgram({ - name: `${prefix}DelegationOrderProgram`, + const DelegationCommandProgram = ZkProgram({ + name: `${prefix}DelegationCommandProgram`, - publicInput: DelegationOrder, + publicInput: DelegationCommand, methods: { sign: { privateInputs: [Ecdsa.provable], - async method(order: DelegationOrder, signature: Ecdsa) { + async method(order: DelegationCommand, signature: Ecdsa) { order.assertSignatureMatches(signature); }, }, }, }); - class DelegationOrderProof - extends ZkProgram.Proof(DelegationOrderProgram) - implements _DelegationOrderProof {} + class DelegationCommandProof + extends ZkProgram.Proof(DelegationCommandProgram) + implements _DelegationCommandProof {} - class DynamicDelegationOrderProof extends DynamicProof { - static override publicInputType = DelegationOrder; + class DynamicDelegationCommandProof extends DynamicProof { + static override publicInputType = DelegationCommand; static override publicOutputType = Void; static override maxProofsVerified = 0 as const; } @@ -173,15 +173,15 @@ export function delegateEvmToMina(prefix: string) { * An order that a particular EVM address has signed to authorize (delegate) * a Mina address to act on its behalf. */ - DelegationOrder, + DelegationCommand, /** * A simple {@link ZkProgram} that proves that a valid signature exists for an - * input {@link DelegationOrder}. + * input {@link DelegationCommand}. */ - DelegationOrderProgram, - /** A verifiable proof of {@link DelegationOrderProgram}'s success. */ - DelegationOrderProof, - // /** A dynamic version of {@link DelegationOrderProof}. */ - // DynamicDelegationOrderProof, <- causes ts4904 until we explicitly declare the return type + DelegationCommandProgram, + /** A verifiable proof of {@link DelegationCommandProgram}'s success. */ + DelegationCommandProof, + // /** A dynamic version of {@link DelegationCommandProof}. */ + // DynamicDelegationCommandProof, <- causes ts4904 until we explicitly declare the return type }; } From 5d2060b984b4ab8d9a5095b275f25a848e33c43a Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Mon, 29 Jul 2024 17:03:20 -0700 Subject: [PATCH 31/33] Add MinaDelegationCache class and worker scaffolding to support it --- .../batcher-transaction-poster/tsconfig.json | 2 +- packages/batcher/runtime/tsconfig.json | 2 +- packages/batcher/utils/tsconfig.json | 2 +- packages/batcher/webserver/tsconfig.json | 2 +- .../paima-sdk/paima-mw-core/tsconfig.json | 2 +- .../paima-sdk/paima-providers/package.json | 6 +- .../paima-sdk/paima-providers/src/mina.ts | 2 + .../paima-providers/src/mina/build_worker.ts | 12 ++ .../paima-providers/src/mina/delegation.ts | 169 ++++++++++++++++++ .../paima-providers/src/mina/mina.worker.ts | 50 ++++++ .../paima-providers/src/mina/txt.d.ts | 5 + .../paima-providers/tsconfig.build.json | 12 -- .../paima-sdk/paima-providers/tsconfig.json | 8 +- packages/paima-sdk/paima-sdk/tsconfig.json | 2 +- 14 files changed, 253 insertions(+), 23 deletions(-) create mode 100644 packages/paima-sdk/paima-providers/src/mina/build_worker.ts create mode 100644 packages/paima-sdk/paima-providers/src/mina/delegation.ts create mode 100644 packages/paima-sdk/paima-providers/src/mina/mina.worker.ts create mode 100644 packages/paima-sdk/paima-providers/src/mina/txt.d.ts delete mode 100644 packages/paima-sdk/paima-providers/tsconfig.build.json diff --git a/packages/batcher/batcher-transaction-poster/tsconfig.json b/packages/batcher/batcher-transaction-poster/tsconfig.json index e61ffd106..ff4c05534 100644 --- a/packages/batcher/batcher-transaction-poster/tsconfig.json +++ b/packages/batcher/batcher-transaction-poster/tsconfig.json @@ -8,7 +8,7 @@ "references": [ { "path": "../utils" }, { "path": "../db" }, - { "path": "../../paima-sdk/paima-providers/tsconfig.build.json" }, + { "path": "../../paima-sdk/paima-providers" }, { "path": "../../paima-sdk/paima-concise/tsconfig.build.json" }, { "path": "../../paima-sdk/paima-utils/tsconfig.build.json" }, ] diff --git a/packages/batcher/runtime/tsconfig.json b/packages/batcher/runtime/tsconfig.json index 2e69a2a30..08eaf0b12 100644 --- a/packages/batcher/runtime/tsconfig.json +++ b/packages/batcher/runtime/tsconfig.json @@ -10,7 +10,7 @@ { "path": "../game-input-validator/" }, { "path": "../utils" }, { "path": "../batcher-transaction-poster" }, - { "path": "../../paima-sdk/paima-providers/tsconfig.build.json" }, + { "path": "../../paima-sdk/paima-providers" }, { "path": "../../paima-sdk/paima-utils/tsconfig.build.json" }, { "path": "../../node-sdk/paima-utils-backend" }, { "path": "../../paima-sdk/paima-mw-core/tsconfig.json" }, diff --git a/packages/batcher/utils/tsconfig.json b/packages/batcher/utils/tsconfig.json index 60459b856..a64763c38 100644 --- a/packages/batcher/utils/tsconfig.json +++ b/packages/batcher/utils/tsconfig.json @@ -7,7 +7,7 @@ "include": ["src/**/*"], "references": [ { "path": "../../paima-sdk/paima-utils/tsconfig.build.json" }, - { "path": "../../paima-sdk/paima-providers/tsconfig.build.json" }, + { "path": "../../paima-sdk/paima-providers" }, { "path": "../../paima-sdk/paima-mw-core/tsconfig.json" }, ] } diff --git a/packages/batcher/webserver/tsconfig.json b/packages/batcher/webserver/tsconfig.json index 73e5db230..e64ccd122 100644 --- a/packages/batcher/webserver/tsconfig.json +++ b/packages/batcher/webserver/tsconfig.json @@ -9,7 +9,7 @@ { "path": "../address-validator/" }, { "path": "../db/" }, { "path": "../utils" }, - { "path": "../../paima-sdk/paima-providers/tsconfig.build.json" }, + { "path": "../../paima-sdk/paima-providers" }, { "path": "../../paima-sdk/paima-concise/tsconfig.build.json" }, { "path": "../../paima-sdk/paima-utils/tsconfig.build.json" }, ] diff --git a/packages/paima-sdk/paima-mw-core/tsconfig.json b/packages/paima-sdk/paima-mw-core/tsconfig.json index 183a10767..b31c3e461 100644 --- a/packages/paima-sdk/paima-mw-core/tsconfig.json +++ b/packages/paima-sdk/paima-mw-core/tsconfig.json @@ -9,6 +9,6 @@ { "path": "../paima-utils/tsconfig.build.json" }, { "path": "../paima-prando" }, { "path": "../paima-concise/tsconfig.build.json" }, - { "path": "../paima-providers/tsconfig.build.json" } + { "path": "../paima-providers" } ] } diff --git a/packages/paima-sdk/paima-providers/package.json b/packages/paima-sdk/paima-providers/package.json index 96eb8bb52..f18d40a7c 100644 --- a/packages/paima-sdk/paima-providers/package.json +++ b/packages/paima-sdk/paima-providers/package.json @@ -5,8 +5,8 @@ "access": "public" }, "description": "Library for dApp connection for blockchains supported by Paima", - "main": "build/index.js", "type": "module", + "main": "build/index.js", "types": "build/index.d.ts", "files": [ "/build" @@ -20,7 +20,9 @@ "homepage": "https://docs.paimastudios.com", "scripts": { "lint:eslint": "eslint .", - "build": "tsc --build tsconfig.build.json", + "build": "tsc", + "postbuild": "node build/mina/build_worker.js", + "pretest": "npm run build", "test": "NODE_OPTIONS=--experimental-vm-modules jest --runInBand" }, "dependencies": { diff --git a/packages/paima-sdk/paima-providers/src/mina.ts b/packages/paima-sdk/paima-providers/src/mina.ts index 9b040f7b8..f0e066d74 100644 --- a/packages/paima-sdk/paima-providers/src/mina.ts +++ b/packages/paima-sdk/paima-providers/src/mina.ts @@ -18,6 +18,8 @@ import { import { getWindow } from './window.js'; import type AuroMinaApi from '@aurowallet/mina-provider'; +export { MinaDelegationCache } from './mina/delegation.js'; + export type MinaApi = AuroMinaApi; type MinaAddress = any; diff --git a/packages/paima-sdk/paima-providers/src/mina/build_worker.ts b/packages/paima-sdk/paima-providers/src/mina/build_worker.ts new file mode 100644 index 000000000..22dee9538 --- /dev/null +++ b/packages/paima-sdk/paima-providers/src/mina/build_worker.ts @@ -0,0 +1,12 @@ +import { build } from 'esbuild'; +import { dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const root = dirname(fileURLToPath(import.meta.url)); + +await build({ + entryPoints: [`${root}/mina.worker.js`], + outfile: `${root}/mina.worker.txt`, + bundle: true, + format: 'esm', +}); diff --git a/packages/paima-sdk/paima-providers/src/mina/delegation.ts b/packages/paima-sdk/paima-providers/src/mina/delegation.ts new file mode 100644 index 000000000..efacf7115 --- /dev/null +++ b/packages/paima-sdk/paima-providers/src/mina/delegation.ts @@ -0,0 +1,169 @@ +import { DynamicProof, JsonProof, PrivateKey, PublicKey, VerificationKey } from 'o1js'; +import { extractPublicKey } from '@metamask/eth-sig-util'; +import { delegateEvmToMina } from '@paima/mina-delegation'; + +import { EvmInjectedConnector } from '../index.js'; + +import type { Methods, InitParams } from './mina.worker.js'; +import minaWorkerCode from './mina.worker.txt'; + +const workerBlobUrl = URL.createObjectURL(new Blob([minaWorkerCode], { type: 'text/javascript' })); + +class MinaWorker { + private readonly worker; + private readonly ready: Promise; + private readonly pending = new Map>(); + private next = 1; + + constructor(initParams: InitParams) { + // Needed otherwise async import() of the real script will result in + // onmessage event handler being added too late and our call being dropped. + const readyEvent = Promise.withResolvers(); + this.pending.set(0, readyEvent); + this.ready = readyEvent.promise; + + this.worker = new Worker(workerBlobUrl + '#' + new URLSearchParams(initParams), { + type: 'module', + }); + this.worker.addEventListener('error', console.error); + this.worker.addEventListener('messageerror', console.error); + this.worker.addEventListener('message', event => { + this.pending.get(event.data.id)?.resolve(event.data.result); + this.pending.delete(event.data.id); + }); + } + + method(method: M): Methods[M] { + return (async (...args: unknown[]) => { + await this.ready; + + const replyEvent = Promise.withResolvers(); + const id = this.next++; + this.pending.set(id, replyEvent); + this.worker.postMessage({ id, method, args }); + return await replyEvent.promise; + }) as Methods[M]; + } +} + +interface MinaDelegationCacheStorage { + privateKey?: string; + signature?: string; + verificationKey?: { + data: string; + hash: string; + }; + proof?: JsonProof; +} + +/** Caching Mina DelegationCommand proof generator. */ +export class MinaDelegationCache { + /** The randomly-generated Mina private key, for use. */ + readonly privateKey: PrivateKey; + /** The corresponding public key, which gets signed. */ + readonly publicKey: PublicKey; + /** The signed order. May be rejected by user cancelling. */ + readonly signature: Promise; + /** The verification key which can be used by {@link DynamicProof}. */ + readonly verificationKey: Promise; + /** The serialized ZK proof of the signature. */ + readonly proof: Promise; + + constructor({ + gameName, + onRejectSignature, + }: { + /** The proof namespace, like "My Game login: ". Shown in the signature prompt and used as the storage key. */ + gameName: string; + /** If provided, called when the user declines to sign. */ + onRejectSignature?: (err: unknown, askAgain: () => void) => void; + }) { + const prefix = `${gameName} login: `; + const storageKey = `MinaDelegationCache(${gameName})`; + const storage: MinaDelegationCacheStorage = JSON.parse( + localStorage.getItem(storageKey) ?? '{}' + ); + const { DelegationCommand } = delegateEvmToMina(prefix); + + // 1. key + if (storage.privateKey) { + this.privateKey = PrivateKey.fromBase58(storage.privateKey); + } else { + this.privateKey = PrivateKey.random(); + storage.privateKey = this.privateKey.toBase58(); + localStorage.setItem(storageKey, JSON.stringify(storage)); + } + const target = (this.publicKey = this.privateKey.toPublicKey()); + + // 2. signature + const signaturePromise = (this.signature = (async () => { + if (!storage.signature) { + const provider = await EvmInjectedConnector.instance().connectSimple({ + gameName, + gameChainId: undefined, + }); + const data = DelegationCommand.bytesToSign({ target }); + const stringData = new TextDecoder().decode(data); + if (onRejectSignature) { + while (true) { + try { + storage.signature = await provider.signMessage(stringData); + break; + } catch (err) { + const { promise, resolve } = Promise.withResolvers(); + onRejectSignature(err, resolve); + await promise; + } + } + } else { + storage.signature = await provider.signMessage(stringData); + } + localStorage.setItem(storageKey, JSON.stringify(storage)); + } + return storage.signature; + })()); + + // Lazy-initialize the background thread only if we need it. + let worker: MinaWorker; + function getWorker() { + return (worker ??= new MinaWorker({ prefix })); + } + + // 3. verification key + const vkPromise = (this.verificationKey = (async () => { + // If the VK isn't in local storage, start compiling concurrently + // with waiting for the signature. Also do this if !storage.proof because + // we need to call .compile() before proving. + if (!storage.verificationKey || !storage.proof) { + storage.verificationKey = JSON.parse(await getWorker().method('compile')()); + localStorage.setItem(storageKey, JSON.stringify(storage)); + } + // NB: fromJSON's signature accepts `string` but it actually wants an object, + // so do a horrifying cast. + return VerificationKey.fromJSON(storage.verificationKey as unknown as string /* wtf? */); + })()); + + // 3. proof + this.proof = (async () => { + if (!storage.proof) { + // Wait on dependencies. + await vkPromise; + const signature = await signaturePromise; + + // Turn the signature into a DelegationCommand and sign it. + const data = DelegationCommand.bytesToSign({ target }); + const ethPublicKey = extractPublicKey({ data, signature }); + const proof = await getWorker().method('sign')({ + target: target.toBase58(), + signer: ethPublicKey, + signature: signature, + }); + + // Save it back. + storage.proof = proof; + localStorage.setItem(storageKey, JSON.stringify(storage)); + } + return storage.proof; + })(); + } +} diff --git a/packages/paima-sdk/paima-providers/src/mina/mina.worker.ts b/packages/paima-sdk/paima-providers/src/mina/mina.worker.ts new file mode 100644 index 000000000..8e406dd7e --- /dev/null +++ b/packages/paima-sdk/paima-providers/src/mina/mina.worker.ts @@ -0,0 +1,50 @@ +import { JsonProof, PublicKey } from 'o1js'; +import { delegateEvmToMina, Ecdsa, Secp256k1 } from '@paima/mina-delegation'; + +export type Methods = typeof methods; +export type InitParams = { + prefix: string; +}; + +const initParams = Object.fromEntries(new URLSearchParams(new URL(import.meta.url).hash.substring(1))) as InitParams; +const { DelegationCommand, DelegationCommandProgram } = delegateEvmToMina(initParams.prefix); + +const methods = { + async compile(): Promise { + console.time('DelegationCommandProgram.compile'); + const { verificationKey } = await DelegationCommandProgram.compile(); + console.timeEnd('DelegationCommandProgram.compile'); + // NB: do not use VerificationKey.toJSON as it just returns the "data" field + // which is NOT the format that fromJSON expects. + return JSON.stringify(verificationKey); + }, + + async sign({ + target, + signer, + signature, + }: { + target: string; + signer: string; + signature: string; + }): Promise { + console.time('DelegationCommandProgram.sign'); + const proof = await DelegationCommandProgram.sign( + new DelegationCommand({ + target: PublicKey.fromBase58(target), + signer: Secp256k1.fromHex(signer), + }), + Ecdsa.fromHex(signature) + ); + console.timeEnd('DelegationCommandProgram.sign'); + return proof.toJSON(); + }, +} as const; + +self.addEventListener('message', async event => { + const { id, method, args } = event.data; + const handler = methods[method as keyof Methods] as (...args: unknown[]) => unknown; + const result = await handler(...args); + postMessage({ id, result }); +}); +postMessage({ id: 0 }); // indicate ready diff --git a/packages/paima-sdk/paima-providers/src/mina/txt.d.ts b/packages/paima-sdk/paima-providers/src/mina/txt.d.ts new file mode 100644 index 000000000..91d8830be --- /dev/null +++ b/packages/paima-sdk/paima-providers/src/mina/txt.d.ts @@ -0,0 +1,5 @@ +// https://esbuild.github.io/content-types/#text +declare module '*.txt' { + declare const code: string; + export default code; +} diff --git a/packages/paima-sdk/paima-providers/tsconfig.build.json b/packages/paima-sdk/paima-providers/tsconfig.build.json deleted file mode 100644 index cb830922e..000000000 --- a/packages/paima-sdk/paima-providers/tsconfig.build.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "build", - "resolveJsonModule": true - }, - "include": [ - "src/**/*", - "src/**/*.json" - ] -} \ No newline at end of file diff --git a/packages/paima-sdk/paima-providers/tsconfig.json b/packages/paima-sdk/paima-providers/tsconfig.json index 3d0ed8e55..7a2efcb6f 100644 --- a/packages/paima-sdk/paima-providers/tsconfig.json +++ b/packages/paima-sdk/paima-providers/tsconfig.json @@ -1,12 +1,14 @@ { "extends": "../tsconfig.json", "compilerOptions": { - "rootDir": ".", - "noEmit": true, + "rootDir": "src", + "outDir": "build", + "resolveJsonModule": true }, - "include": ["test/**/*", "src/**/*"], + "include": ["src/**/*", "src/**/*.json"], "references": [ { "path": "../paima-crypto/tsconfig.build.json" }, { "path": "../paima-utils/tsconfig.build.json" }, + { "path": "../../contracts/mina-delegation/tsconfig.json" }, ] } diff --git a/packages/paima-sdk/paima-sdk/tsconfig.json b/packages/paima-sdk/paima-sdk/tsconfig.json index 2711d49d2..2a77a187d 100644 --- a/packages/paima-sdk/paima-sdk/tsconfig.json +++ b/packages/paima-sdk/paima-sdk/tsconfig.json @@ -14,7 +14,7 @@ { "path": "../paima-prando" }, { "path": "../paima-utils/tsconfig.build.json" }, { "path": "../paima-crypto/tsconfig.build.json" }, - { "path": "../paima-providers/tsconfig.build.json" }, + { "path": "../paima-providers" }, { "path": "../paima-concise/tsconfig.build.json" }, { "path": "../paima-executors" }, { "path": "../paima-mw-core" }, From 1cf642b5f9c4bfa60cb3cc40a0c12db8324ae247 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Mon, 29 Jul 2024 17:41:04 -0700 Subject: [PATCH 32/33] Revert IProvider.signMessage to require string, but impls still support Uint8Array --- packages/paima-sdk/paima-providers/src/IProvider.ts | 2 +- packages/paima-sdk/paima-providers/src/mina.ts | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/paima-sdk/paima-providers/src/IProvider.ts b/packages/paima-sdk/paima-providers/src/IProvider.ts index 9759bfe39..86bd4d078 100644 --- a/packages/paima-sdk/paima-providers/src/IProvider.ts +++ b/packages/paima-sdk/paima-providers/src/IProvider.ts @@ -59,6 +59,6 @@ export type AddressAndType = { }; export interface IProvider { getConnection(): ActiveConnection; - signMessage(message: string | Uint8Array): Promise; + signMessage(message: string): Promise; getAddress(): AddressAndType; } diff --git a/packages/paima-sdk/paima-providers/src/mina.ts b/packages/paima-sdk/paima-providers/src/mina.ts index f0e066d74..102a268b5 100644 --- a/packages/paima-sdk/paima-providers/src/mina.ts +++ b/packages/paima-sdk/paima-providers/src/mina.ts @@ -140,16 +140,10 @@ export class MinaProvider implements IProvider { address: this.address, }; }; - signMessage = async (message: string | Uint8Array): Promise => { - // Seems like the aurowallet package requires strings because it uses JSON, - // so throw if the Uint8Array isn't valid UTF-8. - const strMessage = - message instanceof Uint8Array - ? new TextDecoder('utf-8', { fatal: true }).decode(message) - : message; + signMessage = async (message: string): Promise => { // There is no way of choosing the signing account here. At most we could // monitor the changed events and erroring out if it changed. - const signed = await this.conn.api.signMessage({ message: strMessage }); + const signed = await this.conn.api.signMessage({ message }); if ('signature' in signed) { const { signature } = signed; From 0dad8cf26c431da12aca15aad0af400df070614f Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Mon, 29 Jul 2024 17:54:34 -0700 Subject: [PATCH 33/33] Use o1js 1.6.0+ for V2 foreign curves --- package-lock.json | 2 +- packages/contracts/mina-delegation/package.json | 2 +- packages/contracts/mina-delegation/src/index.ts | 11 +++++------ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6b01adb45..dac90a05b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38750,7 +38750,7 @@ "version": "3.1.0", "license": "MIT", "dependencies": { - "o1js": "^1.4.0" + "o1js": "^1.6.0" }, "devDependencies": { "eslint-plugin-o1js": "^0.4.0", diff --git a/packages/contracts/mina-delegation/package.json b/packages/contracts/mina-delegation/package.json index 43d83e7ba..eb91d3d59 100644 --- a/packages/contracts/mina-delegation/package.json +++ b/packages/contracts/mina-delegation/package.json @@ -28,7 +28,7 @@ "o1js" ], "dependencies": { - "o1js": "^1.4.0" + "o1js": "^1.6.0" }, "devDependencies": { "eslint-plugin-o1js": "^0.4.0", diff --git a/packages/contracts/mina-delegation/src/index.ts b/packages/contracts/mina-delegation/src/index.ts index fab212cc9..cb271d243 100644 --- a/packages/contracts/mina-delegation/src/index.ts +++ b/packages/contracts/mina-delegation/src/index.ts @@ -3,22 +3,21 @@ import { Bytes, Crypto, DynamicProof, - ForeignCurve, Proof, PublicKey, Struct, UInt8, Void, ZkProgram, - createEcdsa, - createForeignCurve, + createEcdsaV2, + createForeignCurveV2, } from 'o1js'; // ---------------------------------------------------------------------------- // Common data types /** A Mina foreign curve for Secp256k1, like Ethereum uses. */ -export class Secp256k1 extends createForeignCurve(Crypto.CurveParams.Secp256k1) { +export class Secp256k1 extends createForeignCurveV2(Crypto.CurveParams.Secp256k1) { /** Convert a standard hex public key into this provable struct. */ static fromHex(publicKey: string): Secp256k1 { if (publicKey.startsWith('0x04') && publicKey.length === 4 + 64 + 64) { @@ -38,7 +37,7 @@ export class Secp256k1 extends createForeignCurve(Crypto.CurveParams.Secp256k1) } /** A Mina-provable ECDSA signature on the Secp256k1 curve, like Ethereum uses. */ -export class Ecdsa extends createEcdsa(Secp256k1) { +export class Ecdsa extends createEcdsaV2(Secp256k1) { // o1js-provided fromHex is good enough } @@ -67,7 +66,7 @@ export type DelegationCommandProof = Proof< { // real data target: PublicKey; - signer: ForeignCurve; + signer: Secp256k1; // sort of a type system marker that the interior is a DelegationCommand? assertSignatureMatches(signature: Ecdsa): void; },