From b90faf7601fe85885514699835f5a26709a98f43 Mon Sep 17 00:00:00 2001 From: Yu Jiang Tham Date: Sun, 17 Dec 2023 19:52:48 -0800 Subject: [PATCH 1/8] Add AxiomV2CircuitMetadata encoder --- .gitignore | 3 +- circuit/js/jest.config.js | 16 + circuit/js/package.json | 7 +- circuit/js/pnpm-lock.yaml | 322 ++++++++++++++++++ .../js/src/encoder/axiomV2CircuitMetadata.ts | 85 +++++ circuit/js/src/encoder/index.ts | 1 + circuit/js/src/index.ts | 1 + circuit/js/src/types.ts | 11 + circuit/js/src/utils.ts | 7 + circuit/js/test/unit/encoder.test.ts | 20 ++ circuit/js/tsconfig.json | 3 +- circuit/js/tsconfig.test.json | 40 +++ 12 files changed, 512 insertions(+), 4 deletions(-) create mode 100644 circuit/js/jest.config.js create mode 100644 circuit/js/src/encoder/axiomV2CircuitMetadata.ts create mode 100644 circuit/js/src/encoder/index.ts create mode 100644 circuit/js/test/unit/encoder.test.ts create mode 100644 circuit/js/tsconfig.test.json diff --git a/.gitignore b/.gitignore index 27e89cb2..d13ad7ae 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ node_modules dist data -build \ No newline at end of file +build +debug/ \ No newline at end of file diff --git a/circuit/js/jest.config.js b/circuit/js/jest.config.js new file mode 100644 index 00000000..9c750691 --- /dev/null +++ b/circuit/js/jest.config.js @@ -0,0 +1,16 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +require('dotenv').config({ + path: '.env.local' +}); + +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + modulePathIgnorePatterns: ["/dist/"], + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + {tsconfig: './tsconfig.test.json'}, + ], + }, +}; \ No newline at end of file diff --git a/circuit/js/package.json b/circuit/js/package.json index 0b010b86..40760149 100644 --- a/circuit/js/package.json +++ b/circuit/js/package.json @@ -9,7 +9,8 @@ "types": "web/index.d.ts", "scripts": { "build": "rm -rf ./dist/* && tsc && ts-node scripts/postTsc.js && $npm_execpath run build:docs", - "build:docs": "./scripts/buildDocs.sh" + "build:docs": "./scripts/buildDocs.sh", + "test": "jest" }, "publishConfig": { "directory": "dist" @@ -32,10 +33,14 @@ "viem": "^1.19.9" }, "devDependencies": { + "@types/jest": "^29.5.11", "@types/node": "18.15.13", + "dotenv": "^16.3.1", "dts-bundle-generator": "^9.0.0", "jest": "^29.7.0", + "ts-jest": "^29.1.1", "tslib": "^2.6.2", + "tsx": "^4.6.2", "typescript": "^5.3.2" } } diff --git a/circuit/js/pnpm-lock.yaml b/circuit/js/pnpm-lock.yaml index bc593c94..2d5afbb3 100644 --- a/circuit/js/pnpm-lock.yaml +++ b/circuit/js/pnpm-lock.yaml @@ -28,18 +28,30 @@ dependencies: version: 1.19.11(typescript@5.3.3) devDependencies: + '@types/jest': + specifier: ^29.5.11 + version: 29.5.11 '@types/node': specifier: 18.15.13 version: 18.15.13 + dotenv: + specifier: ^16.3.1 + version: 16.3.1 dts-bundle-generator: specifier: ^9.0.0 version: 9.0.0 jest: specifier: ^29.7.0 version: 29.7.0(@types/node@18.15.13) + ts-jest: + specifier: ^29.1.1 + version: 29.1.1(@babel/core@7.23.5)(jest@29.7.0)(typescript@5.3.3) tslib: specifier: ^2.6.2 version: 2.6.2 + tsx: + specifier: ^4.6.2 + version: 4.6.2 typescript: specifier: ^5.3.2 version: 5.3.3 @@ -442,6 +454,204 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true + /@esbuild/android-arm64@0.18.20: + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@ethereumjs/rlp@4.0.1: resolution: {integrity: sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==} engines: {node: '>=14'} @@ -833,6 +1043,13 @@ packages: '@types/istanbul-lib-report': 3.0.3 dev: true + /@types/jest@29.5.11: + resolution: {integrity: sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==} + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + dev: true + /@types/node@18.15.13: resolution: {integrity: sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==} @@ -1044,6 +1261,13 @@ packages: update-browserslist-db: 1.0.13(browserslist@4.22.2) dev: true + /bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + dependencies: + fast-json-stable-stringify: 2.1.0 + dev: true + /bs58@5.0.0: resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==} dependencies: @@ -1246,6 +1470,11 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true + /dotenv@16.3.1: + resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} + engines: {node: '>=12'} + dev: true + /dts-bundle-generator@9.0.0: resolution: {integrity: sha512-4XPDOb+F1fkPrvqFL4hnNW2Edc3uiFxQDsK8gUMP8/45+QkA+dSOH0ZWZCKXEjA3ITVbPGV1li8oP8JE4Z/Y0g==} engines: {node: '>=14.0.0'} @@ -1274,6 +1503,36 @@ packages: is-arrayish: 0.2.1 dev: true + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: true + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -1445,6 +1704,12 @@ packages: engines: {node: '>=10'} dev: true + /get-tsconfig@4.7.2: + resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + dependencies: + resolve-pkg-maps: 1.0.0 + dev: true + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: @@ -2085,6 +2350,10 @@ packages: p-locate: 4.1.0 dev: true + /lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + dev: true + /lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} dependencies: @@ -2105,6 +2374,10 @@ packages: semver: 7.5.4 dev: true + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + /makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} dependencies: @@ -2344,6 +2617,10 @@ packages: engines: {node: '>=8'} dev: true + /resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + dev: true + /resolve.exports@2.0.2: resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} engines: {node: '>=10'} @@ -2525,6 +2802,40 @@ packages: engines: {node: '>=0.6'} dev: false + /ts-jest@29.1.1(@babel/core@7.23.5)(jest@29.7.0)(typescript@5.3.3): + resolution: {integrity: sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@babel/core': 7.23.5 + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@18.15.13) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.5.4 + typescript: 5.3.3 + yargs-parser: 21.1.1 + dev: true + /tslib@2.4.0: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} dev: false @@ -2533,6 +2844,17 @@ packages: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} dev: true + /tsx@4.6.2: + resolution: {integrity: sha512-QPpBdJo+ZDtqZgAnq86iY/PD2KYCUPSUGIunHdGwyII99GKH+f3z3FZ8XNFLSGQIA4I365ui8wnQpl8OKLqcsg==} + engines: {node: '>=18.0.0'} + hasBin: true + dependencies: + esbuild: 0.18.20 + get-tsconfig: 4.7.2 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} diff --git a/circuit/js/src/encoder/axiomV2CircuitMetadata.ts b/circuit/js/src/encoder/axiomV2CircuitMetadata.ts new file mode 100644 index 00000000..02ba929e --- /dev/null +++ b/circuit/js/src/encoder/axiomV2CircuitMetadata.ts @@ -0,0 +1,85 @@ +import { concat, encodePacked, pad } from "viem"; +import { resizeArray } from "../utils"; +import { AxiomV2CircuitMetadataParams } from "../types"; + + +export const encodeAxiomV2CircuitMetadata = (params: AxiomV2CircuitMetadataParams): string => { + const encodedVersion = encodePacked( + ["uint8"], + [params.version] + ); + let encoded = concat([encodedVersion]); + + const encodedNumInstanceLen = encodePacked( + ["uint8"], + [params.numInstance.length] + ); + const encodedNumInstance = params.numInstance.map((numInstance) => encodePacked( + ["uint32"], + [numInstance] + )); + encoded = concat([encoded, encodedNumInstanceLen, ...encodedNumInstance]); + + const numPhase = params.numChallenge.length; + if (numPhase === 0) { + throw new Error("numChallenge must be non-empty") + } + const encodedNumPhase = encodePacked( + ["uint8"], + [numPhase] + ) + const encodedNumChallenge = params.numChallenge.map((numChallenge) => encodePacked( + ["uint8"], + [numChallenge] + )); + encoded = concat([encoded, encodedNumPhase, ...encodedNumChallenge]); + + const encodedIsAggregation = encodePacked( + ["bool"], + [params.isAggregation] + ); + encoded = concat([encoded, encodedIsAggregation]); + + if (params.numAdvicePerPhase.length > numPhase) { + throw new Error("numAdvicePerPhase must be <= numPhase (numChallenge.length)"); + } + let numAdviceCols = params.numAdvicePerPhase; + const resizedNumAdviceCols = resizeArray(numAdviceCols, numPhase, 0); + const encodedNumAdviceCols = resizedNumAdviceCols.map((adviceCols) => encodePacked( + ["uint16"], + [adviceCols] + )); + encoded = concat([encoded, ...encodedNumAdviceCols]); + + if (params.numLookupAdvicePerPhase.length > numPhase) { + throw new Error("numLookupAdvicePerPhase must be <= numPhase (numChallenge.length)"); + } + let numLookupAdviceCols = params.numLookupAdvicePerPhase; + const resizedNumLookupAdviceCols = resizeArray(numLookupAdviceCols, numPhase, 0); + const encodedNumLookupAdviceCols = resizedNumLookupAdviceCols.map((lookupAdviceCols) => encodePacked( + ["uint8"], + [lookupAdviceCols] + )); + encoded = concat([encoded, ...encodedNumLookupAdviceCols]); + + const encodedNumRlcColumns = encodePacked( + ["uint16"], + [params.numRlcColumns] + ); + const encodedNumFixed = encodePacked( + ["uint8"], + [params.numFixed] + ); + const encodedMaxOutputs = encodePacked( + ["uint16"], + [params.maxOutputs] + ); + encoded = concat([encoded, encodedNumRlcColumns, encodedNumFixed, encodedMaxOutputs]); + + if (encoded.slice(2).length > 64) { + throw new Error(`Circuit metadata cannot be packed into bytes32 (byte length=${encoded.slice(2).length/2})`); + } + encoded = pad(encoded, {size: 32, dir: "right"}); + + return encoded; +} diff --git a/circuit/js/src/encoder/index.ts b/circuit/js/src/encoder/index.ts new file mode 100644 index 00000000..28cce678 --- /dev/null +++ b/circuit/js/src/encoder/index.ts @@ -0,0 +1 @@ +export * from "./axiomV2CircuitMetadata"; \ No newline at end of file diff --git a/circuit/js/src/index.ts b/circuit/js/src/index.ts index f1b3f4e4..bc3ee5d0 100644 --- a/circuit/js/src/index.ts +++ b/circuit/js/src/index.ts @@ -1,5 +1,6 @@ export * from "./subquery"; export * from "./circuitRunner"; export * from "./cliHandler"; +export * from "./encoder"; export { CircuitValue, CircuitValue256 } from "@axiom-crypto/halo2-lib-js"; export * from "@axiom-crypto/halo2-lib-js/halo2lib/functions"; \ No newline at end of file diff --git a/circuit/js/src/types.ts b/circuit/js/src/types.ts index d2cc4a58..651a45f4 100644 --- a/circuit/js/src/types.ts +++ b/circuit/js/src/types.ts @@ -10,3 +10,14 @@ export type RawInput = { [P in keyof T]: ToRawInput; }; +export interface AxiomV2CircuitMetadataParams { + version: number; + numInstance: number[]; + numChallenge: number[]; + isAggregation: boolean; + numAdvicePerPhase: number[]; + numLookupAdvicePerPhase: number[]; + numRlcColumns: number; + numFixed: number; + maxOutputs: number; + }; \ No newline at end of file diff --git a/circuit/js/src/utils.ts b/circuit/js/src/utils.ts index fe3fd849..1c599cff 100644 --- a/circuit/js/src/utils.ts +++ b/circuit/js/src/utils.ts @@ -75,4 +75,11 @@ export const byteArrayToBase64 = (byteArray: Uint8Array) => { export const base64ToByteArray = (base64: string): Uint8Array => { return Buffer.from(base64, 'base64'); +} + +export function resizeArray(arr: any[], size: number, defaultValue: any): any[] { + if (arr.length < size) { + return arr.concat(Array(size - arr.length).fill(defaultValue)); + } + return arr.slice(0, size); } \ No newline at end of file diff --git a/circuit/js/test/unit/encoder.test.ts b/circuit/js/test/unit/encoder.test.ts new file mode 100644 index 00000000..0d7b8eaa --- /dev/null +++ b/circuit/js/test/unit/encoder.test.ts @@ -0,0 +1,20 @@ +import { encodeAxiomV2CircuitMetadata } from "../../src/encoder"; + +describe("Encoder", () => { + test("Encode AxiomV2CircuitMetadata", () => { + const DEFAULT_METADATA = { + version: 0, + numAdvicePerPhase: [4], + numLookupAdvicePerPhase: [1], + numRlcColumns: 0, + numFixed: 1, + numInstance: [128*2 + 128*16], + numChallenge: [0], + maxOutputs: 128, + isAggregation: false, + } + const encoded = encodeAxiomV2CircuitMetadata(DEFAULT_METADATA); + const expected = "0x0001000009000100000004010000010080000000000000000000000000000000"; + expect(encoded).toEqual(expected); + }); +}); \ No newline at end of file diff --git a/circuit/js/tsconfig.json b/circuit/js/tsconfig.json index 99258b28..3dd537fa 100644 --- a/circuit/js/tsconfig.json +++ b/circuit/js/tsconfig.json @@ -20,8 +20,7 @@ "skipLibCheck": true, "outDir": "dist", "types": [ - "node", - "jest" + "node" ] }, "include": [ diff --git a/circuit/js/tsconfig.test.json b/circuit/js/tsconfig.test.json new file mode 100644 index 00000000..99258b28 --- /dev/null +++ b/circuit/js/tsconfig.test.json @@ -0,0 +1,40 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "CommonJS", + "moduleResolution": "node", + "declaration": true, + "declarationMap": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "importHelpers": true, + "lib": [ + "es2020", + "es6" + ], + "preserveSymlinks": true, + "preserveWatchOutput": true, + "pretty": false, + "strict": true, + "sourceMap": true, + "skipLibCheck": true, + "outDir": "dist", + "types": [ + "node", + "jest" + ] + }, + "include": [ + "src/**/*.ts", + "src/**/*.json" + ], + "exclude": [ + "dist", + "node_modules", + "scripts", + "test" + ], + "ts-node": { + "esm": true + } +} From e2ee96f77c68c9fb28a856bfc6e2dea4651c8c7b Mon Sep 17 00:00:00 2001 From: Yu Jiang Tham Date: Mon, 18 Dec 2023 20:58:46 -0800 Subject: [PATCH 2/8] Update computeQuery encoding for circuit metadata --- .../js/src/encoder/axiomV2CircuitMetadata.ts | 8 +++-- circuit/js/src/scaffold.ts | 33 ++++++++++++++++--- circuit/js/src/utils.ts | 2 +- .../test/unit/circuits/7_balance.circuit.ts | 31 +++++++++++++++++ circuit/js/test/unit/encoder.test.ts | 15 ++++++--- circuit/js/test/unit/scaffold.test.ts | 26 +++++++++++++++ client/src/js/index.ts | 2 +- client/src/web/index.ts | 2 +- 8 files changed, 105 insertions(+), 14 deletions(-) create mode 100644 circuit/js/test/unit/circuits/7_balance.circuit.ts create mode 100644 circuit/js/test/unit/scaffold.test.ts diff --git a/circuit/js/src/encoder/axiomV2CircuitMetadata.ts b/circuit/js/src/encoder/axiomV2CircuitMetadata.ts index 02ba929e..c4ce2880 100644 --- a/circuit/js/src/encoder/axiomV2CircuitMetadata.ts +++ b/circuit/js/src/encoder/axiomV2CircuitMetadata.ts @@ -4,6 +4,10 @@ import { AxiomV2CircuitMetadataParams } from "../types"; export const encodeAxiomV2CircuitMetadata = (params: AxiomV2CircuitMetadataParams): string => { + // JS client overrides + params.numChallenge = [0]; + params.isAggregation = false; + const encodedVersion = encodePacked( ["uint8"], [params.version] @@ -44,7 +48,7 @@ export const encodeAxiomV2CircuitMetadata = (params: AxiomV2CircuitMetadataParam throw new Error("numAdvicePerPhase must be <= numPhase (numChallenge.length)"); } let numAdviceCols = params.numAdvicePerPhase; - const resizedNumAdviceCols = resizeArray(numAdviceCols, numPhase, 0); + const resizedNumAdviceCols = resizeArray(numAdviceCols, numPhase, 0); const encodedNumAdviceCols = resizedNumAdviceCols.map((adviceCols) => encodePacked( ["uint16"], [adviceCols] @@ -55,7 +59,7 @@ export const encodeAxiomV2CircuitMetadata = (params: AxiomV2CircuitMetadataParam throw new Error("numLookupAdvicePerPhase must be <= numPhase (numChallenge.length)"); } let numLookupAdviceCols = params.numLookupAdvicePerPhase; - const resizedNumLookupAdviceCols = resizeArray(numLookupAdviceCols, numPhase, 0); + const resizedNumLookupAdviceCols = resizeArray(numLookupAdviceCols, numPhase, 0); const encodedNumLookupAdviceCols = resizedNumLookupAdviceCols.map((lookupAdviceCols) => encodePacked( ["uint8"], [lookupAdviceCols] diff --git a/circuit/js/src/scaffold.ts b/circuit/js/src/scaffold.ts index 789acbe2..f665a18a 100644 --- a/circuit/js/src/scaffold.ts +++ b/circuit/js/src/scaffold.ts @@ -1,10 +1,11 @@ import { CircuitConfig, Halo2LibWasm } from "@axiom-crypto/halo2-wasm/web"; import { keccak256 } from "ethers"; import { base64ToByteArray, byteArrayToBase64, convertToBytes, convertToBytes32 } from "./utils"; -import { encodePacked } from "viem"; +import { concat, encodePacked, zeroHash } from "viem"; import { AxiomCircuitRunner } from "./circuitRunner"; import { AxiomV2Callback, + AxiomV2CircuitConstant, AxiomV2ComputeQuery, DataSubquery, } from "@axiom-crypto/tools"; @@ -15,6 +16,7 @@ import { import { BaseCircuitScaffold } from "@axiom-crypto/halo2-wasm/shared/scaffold"; import { DEFAULT_CIRCUIT_CONFIG } from "./constants"; import { RawInput } from "./types"; +import { encodeAxiomV2CircuitMetadata } from "./js"; export abstract class AxiomBaseCircuitScaffold extends BaseCircuitScaffold { protected numInstances: number; @@ -85,6 +87,24 @@ export abstract class AxiomBaseCircuitScaffold extends BaseCircuitScaffold { return schema as string; } + prependCircuitMetadata(config: CircuitConfig, partialVk: string[]): string[] { + if (config.numInstance !== 1) { + throw new Error("`numInstance` only supported value is 1"); + } + const encodedCircuitMetadata = encodeAxiomV2CircuitMetadata({ + version: 0, + numInstance: [config.numInstance], + numChallenge: [0], + isAggregation: false, + numAdvicePerPhase: [config.numAdvice], + numLookupAdvicePerPhase: [config.numLookupAdvice], + numRlcColumns: 0, + numFixed: 1, + maxOutputs: AxiomV2CircuitConstant.UserMaxOutputs, + }); + return [encodedCircuitMetadata, ...partialVk]; + } + async compile(inputs: RawInput) { this.newCircuitFromConfig(this.config); this.timeStart("Witness generation"); @@ -133,11 +153,16 @@ export abstract class AxiomBaseCircuitScaffold extends BaseCircuitScaffold { buildComputeQuery() { const vk = this.getPartialVk(); const vkBytes = convertToBytes32(vk); - const computeProof = this.getComputeProof(); + const onchainVkey = this.prependCircuitMetadata(this.config, vkBytes); + + const computeProofBase = this.getComputeProof() as `0x${string}`; + const computeAccumulator = concat([zeroHash, zeroHash]); + const computeProof = concat([computeAccumulator, computeProofBase]); + const computeQuery: AxiomV2ComputeQuery = { k: this.config.k, - vkey: vkBytes, - computeProof: computeProof, + vkey: onchainVkey, + computeProof, resultLen: this.numInstances / 2, }; this.computeQuery = computeQuery; diff --git a/circuit/js/src/utils.ts b/circuit/js/src/utils.ts index 1c599cff..87787597 100644 --- a/circuit/js/src/utils.ts +++ b/circuit/js/src/utils.ts @@ -77,7 +77,7 @@ export const base64ToByteArray = (base64: string): Uint8Array => { return Buffer.from(base64, 'base64'); } -export function resizeArray(arr: any[], size: number, defaultValue: any): any[] { +export function resizeArray(arr: T[], size: number, defaultValue: T): T[] { if (arr.length < size) { return arr.concat(Array(size - arr.length).fill(defaultValue)); } diff --git a/circuit/js/test/unit/circuits/7_balance.circuit.ts b/circuit/js/test/unit/circuits/7_balance.circuit.ts new file mode 100644 index 00000000..d61ad344 --- /dev/null +++ b/circuit/js/test/unit/circuits/7_balance.circuit.ts @@ -0,0 +1,31 @@ +import { +CircuitValue, +CircuitValue256, +getAccount, +addToCallback, +add, +or, +} from "../../../src"; + +export const inputs = { + address: "0x897dDbe14c9C7736EbfDC58461355697FbF70048", + claimedBlockNumber: 9173677, +}; +export type CircuitInputType = typeof inputs; +export interface CircuitInputs extends CircuitInputType { } +export interface CircuitValueInputs { + address: CircuitValue; + claimedBlockNumber: CircuitValue; +} +export const circuit = async ({ + address, + claimedBlockNumber, +}: CircuitValueInputs) => { + + for (let i = 0; i < 7; i++) { + const acct = getAccount(add(claimedBlockNumber, i), address); + const balance = await acct.balance(); + addToCallback(balance); + } + +}; \ No newline at end of file diff --git a/circuit/js/test/unit/encoder.test.ts b/circuit/js/test/unit/encoder.test.ts index 0d7b8eaa..99037517 100644 --- a/circuit/js/test/unit/encoder.test.ts +++ b/circuit/js/test/unit/encoder.test.ts @@ -1,16 +1,21 @@ import { encodeAxiomV2CircuitMetadata } from "../../src/encoder"; +import { AxiomV2CircuitConstant } from "@axiom-crypto/tools"; describe("Encoder", () => { test("Encode AxiomV2CircuitMetadata", () => { + const SUBQUERY_RESULT_LEN = 16; const DEFAULT_METADATA = { version: 0, - numAdvicePerPhase: [4], - numLookupAdvicePerPhase: [1], + numAdvicePerPhase: [AxiomV2CircuitConstant.UserAdviceCols], + numLookupAdvicePerPhase: [AxiomV2CircuitConstant.UserLookupAdviceCols], numRlcColumns: 0, - numFixed: 1, - numInstance: [128*2 + 128*16], + numFixed: AxiomV2CircuitConstant.UserFixedCols, + numInstance: [ + AxiomV2CircuitConstant.UserMaxOutputs * AxiomV2CircuitConstant.UserResultFieldElements + + AxiomV2CircuitConstant.UserMaxSubqueries * SUBQUERY_RESULT_LEN + ], numChallenge: [0], - maxOutputs: 128, + maxOutputs: AxiomV2CircuitConstant.UserMaxOutputs, isAggregation: false, } const encoded = encodeAxiomV2CircuitMetadata(DEFAULT_METADATA); diff --git a/circuit/js/test/unit/scaffold.test.ts b/circuit/js/test/unit/scaffold.test.ts new file mode 100644 index 00000000..ed481674 --- /dev/null +++ b/circuit/js/test/unit/scaffold.test.ts @@ -0,0 +1,26 @@ +import { concat, zeroHash } from "viem"; +import { AxiomBaseCircuit } from "../../src/js"; +import { circuit } from "./circuits/7_balance.circuit"; + +describe("Scaffold", () => { + test("Build computeQuery", async () => { + const testCircuit = new AxiomBaseCircuit({ + provider: "http://localhost:8545", + f: circuit, + inputSchema: `{ + "address": "CircuitValue", + "claimedBlockNumber": "CircuitValue" + }`, + chainId: 5, + mock: true, + }); + const defaultInputs = { + address: "0x897dDbe14c9C7736EbfDC58461355697FbF70048", + claimedBlockNumber: 9173677, + }; + const _artifact = await testCircuit.compile(defaultInputs); + const computeQuery = await testCircuit.run(defaultInputs); + expect(computeQuery.vkey[0]).toEqual("0x0001000000010100000004010000010080000000000000000000000000000000"); + expect(computeQuery.computeProof.slice(2).slice(0,128)).toEqual(concat([zeroHash, zeroHash]).slice(2)); + }, 30000); +}); \ No newline at end of file diff --git a/client/src/js/index.ts b/client/src/js/index.ts index 078ac320..c2218aa2 100644 --- a/client/src/js/index.ts +++ b/client/src/js/index.ts @@ -10,7 +10,7 @@ export class AxiomCircuit extends AxiomBaseCircuit { mock?: boolean, chainId?: number | string | bigint, shouldTime?: boolean -}) { + }) { super(inputs); } diff --git a/client/src/web/index.ts b/client/src/web/index.ts index 8504a5f4..6550ad74 100644 --- a/client/src/web/index.ts +++ b/client/src/web/index.ts @@ -10,7 +10,7 @@ export class AxiomCircuit extends AxiomBaseCircuit { mock?: boolean, chainId?: number | string | bigint, shouldTime?: boolean -}) { + }) { super(inputs); } From e46a70e8b5cacfaf6ac32a75f3893b2a00ace6ae Mon Sep 17 00:00:00 2001 From: Yu Jiang Tham Date: Mon, 18 Dec 2023 21:02:19 -0800 Subject: [PATCH 3/8] Update gh actions --- .github/workflows/test.yml | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c003210b..35fad70c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,8 +7,34 @@ on: pull_request: jobs: - build: - name: Jest Testing + circuit-js: + name: JS Circuit Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Use Node.js 18.x + uses: actions/setup-node@v2 + with: + node-version: 18.x + + - name: Install Harness dependencies + working-directory: ./circuit/js + run: npm install + + - name: Run Integration Tests + working-directory: ./circuit/js + run: | + export PROVIDER_URI_GOERLI=${{ secrets.PROVIDER_URI_GOERLI }} + ANVIL_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + export PRIVATE_KEY=$ANVIL_PRIVATE_KEY # just needs to be in the correct format, no tx is sent in unit tests + export PRIVATE_KEY_GOERLI=$ANVIL_PRIVATE_KEY + npm run test + + harness: + name: Harness Tests runs-on: ubuntu-latest steps: From ffaba0543b728da7ea89dbe9b1a0f3296da8a9aa Mon Sep 17 00:00:00 2001 From: Yu Jiang Tham Date: Mon, 18 Dec 2023 21:04:46 -0800 Subject: [PATCH 4/8] Fix type issue --- .github/workflows/test.yml | 4 ++-- circuit/js/src/scaffold.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 35fad70c..7be1edf7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,11 +20,11 @@ jobs: with: node-version: 18.x - - name: Install Harness dependencies + - name: Install dependencies working-directory: ./circuit/js run: npm install - - name: Run Integration Tests + - name: Run Unit Tests working-directory: ./circuit/js run: | export PROVIDER_URI_GOERLI=${{ secrets.PROVIDER_URI_GOERLI }} diff --git a/circuit/js/src/scaffold.ts b/circuit/js/src/scaffold.ts index f665a18a..dec9b334 100644 --- a/circuit/js/src/scaffold.ts +++ b/circuit/js/src/scaffold.ts @@ -81,7 +81,7 @@ export abstract class AxiomBaseCircuitScaffold extends BaseCircuitScaffold { const vk = convertToBytes32(partialVk); const packed = encodePacked( ["uint8", "uint16", "uint8", "bytes32[]"], - [this.config.k, this.numInstances / 2, vk.length, vk], + [this.config.k, this.numInstances / 2, vk.length, vk as `0x${string}`[]], ); const schema = keccak256(packed); return schema as string; From 0293da4f7fdb9232feb2448b05d03ff1a2e42fd2 Mon Sep 17 00:00:00 2001 From: Yu Jiang Tham Date: Mon, 18 Dec 2023 21:11:26 -0800 Subject: [PATCH 5/8] Set provider from environment --- circuit/js/test/unit/scaffold.test.ts | 2 +- harness/test/integration/goerli.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/circuit/js/test/unit/scaffold.test.ts b/circuit/js/test/unit/scaffold.test.ts index ed481674..f16de6eb 100644 --- a/circuit/js/test/unit/scaffold.test.ts +++ b/circuit/js/test/unit/scaffold.test.ts @@ -5,7 +5,7 @@ import { circuit } from "./circuits/7_balance.circuit"; describe("Scaffold", () => { test("Build computeQuery", async () => { const testCircuit = new AxiomBaseCircuit({ - provider: "http://localhost:8545", + provider: process.env.PROVIDER_URI_GOERLI as string, f: circuit, inputSchema: `{ "address": "CircuitValue", diff --git a/harness/test/integration/goerli.test.ts b/harness/test/integration/goerli.test.ts index 7a60522b..b37cba49 100644 --- a/harness/test/integration/goerli.test.ts +++ b/harness/test/integration/goerli.test.ts @@ -3,7 +3,7 @@ import { listDir, makeFileMap } from "../utils"; import { run } from "../../src/run"; describe("Run", () => { - if (process.env.PROVIDER_URI_GOERLI == undefined) { + if (process.env.PROVIDER_URI_GOERLI === undefined) { throw new Error("`PROVIDER_URI_GOERLI` environment variable must be defined"); } From 0a71484649797e4c791c8d6ba7df084866d55904 Mon Sep 17 00:00:00 2001 From: Yu Jiang Tham Date: Mon, 25 Dec 2023 09:25:50 -0800 Subject: [PATCH 6/8] Update metadata based on comments --- .../js/src/encoder/axiomV2CircuitMetadata.ts | 11 +++++------ circuit/js/src/scaffold.ts | 17 +++++++---------- circuit/js/src/types.ts | 2 +- circuit/js/test/unit/encoder.test.ts | 5 +++-- circuit/js/test/unit/scaffold.test.ts | 9 ++++++--- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/circuit/js/src/encoder/axiomV2CircuitMetadata.ts b/circuit/js/src/encoder/axiomV2CircuitMetadata.ts index c4ce2880..a4a757c3 100644 --- a/circuit/js/src/encoder/axiomV2CircuitMetadata.ts +++ b/circuit/js/src/encoder/axiomV2CircuitMetadata.ts @@ -2,7 +2,6 @@ import { concat, encodePacked, pad } from "viem"; import { resizeArray } from "../utils"; import { AxiomV2CircuitMetadataParams } from "../types"; - export const encodeAxiomV2CircuitMetadata = (params: AxiomV2CircuitMetadataParams): string => { // JS client overrides params.numChallenge = [0]; @@ -14,15 +13,15 @@ export const encodeAxiomV2CircuitMetadata = (params: AxiomV2CircuitMetadataParam ); let encoded = concat([encodedVersion]); - const encodedNumInstanceLen = encodePacked( + const encodedNumValuesPerInstanceColumnLen = encodePacked( ["uint8"], - [params.numInstance.length] + [params.numValuesPerInstanceColumn.length] ); - const encodedNumInstance = params.numInstance.map((numInstance) => encodePacked( + const encodedNumValuesPerInstanceColumn = params.numValuesPerInstanceColumn.map((values) => encodePacked( ["uint32"], - [numInstance] + [values] )); - encoded = concat([encoded, encodedNumInstanceLen, ...encodedNumInstance]); + encoded = concat([encoded, encodedNumValuesPerInstanceColumnLen, ...encodedNumValuesPerInstanceColumn]); const numPhase = params.numChallenge.length; if (numPhase === 0) { diff --git a/circuit/js/src/scaffold.ts b/circuit/js/src/scaffold.ts index dec9b334..9807d262 100644 --- a/circuit/js/src/scaffold.ts +++ b/circuit/js/src/scaffold.ts @@ -19,7 +19,7 @@ import { RawInput } from "./types"; import { encodeAxiomV2CircuitMetadata } from "./js"; export abstract class AxiomBaseCircuitScaffold extends BaseCircuitScaffold { - protected numInstances: number; + protected resultLen: number; protected halo2Lib!: Halo2LibWasm; protected provider: string; protected dataQuery: DataSubquery[]; @@ -40,7 +40,7 @@ export abstract class AxiomBaseCircuitScaffold extends BaseCircuitScaffold { shouldTime?: boolean; }) { super(); - this.numInstances = 0; + this.resultLen = 0; this.provider = inputs.provider; this.config = inputs.config ?? DEFAULT_CIRCUIT_CONFIG; this.dataQuery = []; @@ -81,19 +81,16 @@ export abstract class AxiomBaseCircuitScaffold extends BaseCircuitScaffold { const vk = convertToBytes32(partialVk); const packed = encodePacked( ["uint8", "uint16", "uint8", "bytes32[]"], - [this.config.k, this.numInstances / 2, vk.length, vk as `0x${string}`[]], + [this.config.k, this.resultLen, vk.length, vk as `0x${string}`[]], ); const schema = keccak256(packed); return schema as string; } prependCircuitMetadata(config: CircuitConfig, partialVk: string[]): string[] { - if (config.numInstance !== 1) { - throw new Error("`numInstance` only supported value is 1"); - } const encodedCircuitMetadata = encodeAxiomV2CircuitMetadata({ version: 0, - numInstance: [config.numInstance], + numValuesPerInstanceColumn: [2 * this.resultLen + 16 * AxiomV2CircuitConstant.UserMaxSubqueries], numChallenge: [0], isAggregation: false, numAdvicePerPhase: [config.numAdvice], @@ -140,7 +137,7 @@ export abstract class AxiomBaseCircuitScaffold extends BaseCircuitScaffold { this.provider, ).run(this.f, inputs, this.inputSchema, this.results); this.timeEnd("Witness generation"); - this.numInstances = numUserInstances; + this.resultLen = Number(numUserInstances / 2); this.dataQuery = dataQuery; } @@ -163,7 +160,7 @@ export abstract class AxiomBaseCircuitScaffold extends BaseCircuitScaffold { k: this.config.k, vkey: onchainVkey, computeProof, - resultLen: this.numInstances / 2, + resultLen: this.resultLen, }; this.computeQuery = computeQuery; return computeQuery; @@ -181,7 +178,7 @@ export abstract class AxiomBaseCircuitScaffold extends BaseCircuitScaffold { getComputeResults() { const computeResults: string[] = []; const instances = this.getInstances(); - for (let i = 0; i < this.numInstances / 2; i++) { + for (let i = 0; i < this.resultLen; i++) { const instanceHi = BigInt(instances[2 * i]); const instanceLo = BigInt(instances[2 * i + 1]); const instance = instanceHi * BigInt(2 ** 128) + instanceLo; diff --git a/circuit/js/src/types.ts b/circuit/js/src/types.ts index 651a45f4..f6bfaf2d 100644 --- a/circuit/js/src/types.ts +++ b/circuit/js/src/types.ts @@ -12,7 +12,7 @@ export type RawInput = { export interface AxiomV2CircuitMetadataParams { version: number; - numInstance: number[]; + numValuesPerInstanceColumn: number[]; numChallenge: number[]; isAggregation: boolean; numAdvicePerPhase: number[]; diff --git a/circuit/js/test/unit/encoder.test.ts b/circuit/js/test/unit/encoder.test.ts index 99037517..f91b9000 100644 --- a/circuit/js/test/unit/encoder.test.ts +++ b/circuit/js/test/unit/encoder.test.ts @@ -1,16 +1,17 @@ import { encodeAxiomV2CircuitMetadata } from "../../src/encoder"; import { AxiomV2CircuitConstant } from "@axiom-crypto/tools"; +import { AxiomV2CircuitMetadataParams } from "../../src/types"; describe("Encoder", () => { test("Encode AxiomV2CircuitMetadata", () => { const SUBQUERY_RESULT_LEN = 16; - const DEFAULT_METADATA = { + const DEFAULT_METADATA: AxiomV2CircuitMetadataParams = { version: 0, numAdvicePerPhase: [AxiomV2CircuitConstant.UserAdviceCols], numLookupAdvicePerPhase: [AxiomV2CircuitConstant.UserLookupAdviceCols], numRlcColumns: 0, numFixed: AxiomV2CircuitConstant.UserFixedCols, - numInstance: [ + numValuesPerInstanceColumn: [ AxiomV2CircuitConstant.UserMaxOutputs * AxiomV2CircuitConstant.UserResultFieldElements + AxiomV2CircuitConstant.UserMaxSubqueries * SUBQUERY_RESULT_LEN ], diff --git a/circuit/js/test/unit/scaffold.test.ts b/circuit/js/test/unit/scaffold.test.ts index f16de6eb..4c3e4e04 100644 --- a/circuit/js/test/unit/scaffold.test.ts +++ b/circuit/js/test/unit/scaffold.test.ts @@ -1,12 +1,13 @@ import { concat, zeroHash } from "viem"; import { AxiomBaseCircuit } from "../../src/js"; -import { circuit } from "./circuits/7_balance.circuit"; +import { circuit as seven_blance_circuit } from "./circuits/7_balance.circuit"; describe("Scaffold", () => { test("Build computeQuery", async () => { + // This circuit gets a single account's balance 7 times and adds them to the results const testCircuit = new AxiomBaseCircuit({ provider: process.env.PROVIDER_URI_GOERLI as string, - f: circuit, + f: seven_blance_circuit, inputSchema: `{ "address": "CircuitValue", "claimedBlockNumber": "CircuitValue" @@ -20,7 +21,9 @@ describe("Scaffold", () => { }; const _artifact = await testCircuit.compile(defaultInputs); const computeQuery = await testCircuit.run(defaultInputs); - expect(computeQuery.vkey[0]).toEqual("0x0001000000010100000004010000010080000000000000000000000000000000"); + + // numValuesPerInstanceColumn: 2 * 7 + 16 * 128 = 2062 (0x80E) + expect(computeQuery.vkey[0]).toEqual("0x00010000080e0100000004010000010080000000000000000000000000000000"); expect(computeQuery.computeProof.slice(2).slice(0,128)).toEqual(concat([zeroHash, zeroHash]).slice(2)); }, 30000); }); \ No newline at end of file From c39b1125c30f910b0160f1b1af560d07f4d6bf69 Mon Sep 17 00:00:00 2001 From: Yu Jiang Tham Date: Mon, 25 Dec 2023 09:44:38 -0800 Subject: [PATCH 7/8] Add floor --- circuit/js/src/scaffold.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circuit/js/src/scaffold.ts b/circuit/js/src/scaffold.ts index 9807d262..97a7e828 100644 --- a/circuit/js/src/scaffold.ts +++ b/circuit/js/src/scaffold.ts @@ -137,7 +137,7 @@ export abstract class AxiomBaseCircuitScaffold extends BaseCircuitScaffold { this.provider, ).run(this.f, inputs, this.inputSchema, this.results); this.timeEnd("Witness generation"); - this.resultLen = Number(numUserInstances / 2); + this.resultLen = Math.floor(numUserInstances / 2); this.dataQuery = dataQuery; } From d1ad5cf40b0bd6091a1e67dbb595fe9e3ed91fa7 Mon Sep 17 00:00:00 2001 From: Yu Jiang Tham Date: Tue, 26 Dec 2023 14:09:03 -0800 Subject: [PATCH 8/8] Update scaffold equation and tests --- circuit/js/src/scaffold.ts | 9 ++++++++- circuit/js/test/unit/encoder.test.ts | 2 +- circuit/js/test/unit/scaffold.test.ts | 7 +++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/circuit/js/src/scaffold.ts b/circuit/js/src/scaffold.ts index 97a7e828..4d4da1aa 100644 --- a/circuit/js/src/scaffold.ts +++ b/circuit/js/src/scaffold.ts @@ -88,9 +88,13 @@ export abstract class AxiomBaseCircuitScaffold extends BaseCircuitScaffold { } prependCircuitMetadata(config: CircuitConfig, partialVk: string[]): string[] { + const SUBQUERY_RESULT_LEN = 1 + AxiomV2CircuitConstant.MaxSubqueryInputs + AxiomV2CircuitConstant.MaxSubqueryOutputs; const encodedCircuitMetadata = encodeAxiomV2CircuitMetadata({ version: 0, - numValuesPerInstanceColumn: [2 * this.resultLen + 16 * AxiomV2CircuitConstant.UserMaxSubqueries], + numValuesPerInstanceColumn: [ + AxiomV2CircuitConstant.UserMaxOutputs * AxiomV2CircuitConstant.UserResultFieldElements + + AxiomV2CircuitConstant.UserMaxSubqueries * SUBQUERY_RESULT_LEN + ], numChallenge: [0], isAggregation: false, numAdvicePerPhase: [config.numAdvice], @@ -137,6 +141,9 @@ export abstract class AxiomBaseCircuitScaffold extends BaseCircuitScaffold { this.provider, ).run(this.f, inputs, this.inputSchema, this.results); this.timeEnd("Witness generation"); + if (numUserInstances % 2 !== 0) { + throw new Error("numUserInstances must be even"); + } this.resultLen = Math.floor(numUserInstances / 2); this.dataQuery = dataQuery; } diff --git a/circuit/js/test/unit/encoder.test.ts b/circuit/js/test/unit/encoder.test.ts index f91b9000..4ffab0c3 100644 --- a/circuit/js/test/unit/encoder.test.ts +++ b/circuit/js/test/unit/encoder.test.ts @@ -4,7 +4,7 @@ import { AxiomV2CircuitMetadataParams } from "../../src/types"; describe("Encoder", () => { test("Encode AxiomV2CircuitMetadata", () => { - const SUBQUERY_RESULT_LEN = 16; + const SUBQUERY_RESULT_LEN = 1 + AxiomV2CircuitConstant.MaxSubqueryInputs + AxiomV2CircuitConstant.MaxSubqueryOutputs; const DEFAULT_METADATA: AxiomV2CircuitMetadataParams = { version: 0, numAdvicePerPhase: [AxiomV2CircuitConstant.UserAdviceCols], diff --git a/circuit/js/test/unit/scaffold.test.ts b/circuit/js/test/unit/scaffold.test.ts index 4c3e4e04..4edb42a7 100644 --- a/circuit/js/test/unit/scaffold.test.ts +++ b/circuit/js/test/unit/scaffold.test.ts @@ -1,13 +1,13 @@ import { concat, zeroHash } from "viem"; import { AxiomBaseCircuit } from "../../src/js"; -import { circuit as seven_blance_circuit } from "./circuits/7_balance.circuit"; +import { circuit as seven_balance_circuit } from "./circuits/7_balance.circuit"; describe("Scaffold", () => { test("Build computeQuery", async () => { // This circuit gets a single account's balance 7 times and adds them to the results const testCircuit = new AxiomBaseCircuit({ provider: process.env.PROVIDER_URI_GOERLI as string, - f: seven_blance_circuit, + f: seven_balance_circuit, inputSchema: `{ "address": "CircuitValue", "claimedBlockNumber": "CircuitValue" @@ -22,8 +22,7 @@ describe("Scaffold", () => { const _artifact = await testCircuit.compile(defaultInputs); const computeQuery = await testCircuit.run(defaultInputs); - // numValuesPerInstanceColumn: 2 * 7 + 16 * 128 = 2062 (0x80E) - expect(computeQuery.vkey[0]).toEqual("0x00010000080e0100000004010000010080000000000000000000000000000000"); + expect(computeQuery.vkey[0]).toEqual("0x0001000009000100000004010000010080000000000000000000000000000000"); expect(computeQuery.computeProof.slice(2).slice(0,128)).toEqual(concat([zeroHash, zeroHash]).slice(2)); }, 30000); }); \ No newline at end of file