├── .cargo └── config ├── .eslintignore ├── .eslintrc.json ├── .github └── workflows │ └── test.yaml ├── .gitignore ├── .prettierrc ├── Cargo.toml ├── LICENSE ├── README.md ├── babel.config.js ├── package.json └── packages ├── apis ├── .gitignore ├── Cargo.toml ├── README.md ├── package.json ├── src │ ├── decomposed_defs │ │ ├── body_hash.json │ │ ├── email_addr.json │ │ ├── email_domain.json │ │ ├── from_addr.json │ │ ├── from_all.json │ │ ├── message_id.json │ │ ├── subject_all.json │ │ ├── timestamp.json │ │ ├── to_addr.json │ │ └── to_all.json │ ├── extract_substrs.rs │ ├── lib.rs │ ├── padding.rs │ └── wasm.rs └── tests │ ├── airbnb_eml.ts │ └── extract_substr.test.js ├── circom ├── README.md ├── circuits │ ├── common │ │ ├── body_hash.json │ │ ├── body_hash_regex.circom │ │ ├── email_addr.json │ │ ├── email_addr_regex.circom │ │ ├── email_addr_with_name_regex.circom │ │ ├── email_domain.json │ │ ├── email_domain_regex.circom │ │ ├── from_addr_regex.circom │ │ ├── from_all.json │ │ ├── from_all_regex.circom │ │ ├── message_id.json │ │ ├── message_id_regex.circom │ │ ├── reversed_bracket.json │ │ ├── reversed_bracket_regex.circom │ │ ├── reversed_email_addr_with_name_regex.circom │ │ ├── subject_all.json │ │ ├── subject_all_regex.circom │ │ ├── timestamp.json │ │ ├── timestamp_regex.circom │ │ ├── to_addr_regex.circom │ │ ├── to_all.json │ │ └── to_all_regex.circom │ └── regex_helpers.circom ├── package.json ├── pnpm-lock.yaml └── tests │ ├── asterisk.test.js │ ├── body_hash_regex.test.js │ ├── caret.test.js │ ├── circuits │ ├── asterisk1.json │ ├── asterisk1_regex.circom │ ├── asterisk2.json │ ├── asterisk2_regex.circom │ ├── asterisk3.json │ ├── asterisk3_regex.circom │ ├── caret1.json │ ├── caret1_regex.circom │ ├── caret2.json │ ├── caret2_regex.circom │ ├── caret3.json │ ├── caret3_regex.circom │ ├── caret4.json │ ├── caret4_regex.circom │ ├── caret5.json │ ├── caret5_regex.circom │ ├── dollar1.json │ ├── dollar1_regex.circom │ ├── dollar2.json │ ├── dollar2_regex.circom │ ├── dot1.json │ ├── dot1_regex.circom │ ├── dot2.json │ ├── dot2_regex.circom │ ├── international_chars_decomposed.circom │ ├── international_chars_decomposed.json │ ├── invitation_code_with_prefix.json │ ├── invitation_code_with_prefix_regex.circom │ ├── negate1.json │ ├── negate1_regex.circom │ ├── negate2.json │ ├── negate2_regex.circom │ ├── plus1.json │ ├── plus1_regex.circom │ ├── plus2.json │ ├── plus2_regex.circom │ ├── plus3.json │ ├── plus3_regex.circom │ ├── plus4.json │ ├── plus4_regex.circom │ ├── question1.json │ ├── question1_regex.circom │ ├── question2.json │ ├── question2_regex.circom │ ├── question3.json │ ├── question3_regex.circom │ ├── reveal_check1.json │ ├── reveal_check1_regex.circom │ ├── reveal_check2.json │ ├── reveal_check2_regex.circom │ ├── simple_regex.circom │ ├── simple_regex_decomposed.circom │ ├── simple_regex_decomposed.json │ ├── simple_regex_substrs.json │ ├── test_asterisk1_regex.circom │ ├── test_asterisk2_regex.circom │ ├── test_asterisk3_regex.circom │ ├── test_body_hash_regex.circom │ ├── test_caret1_regex.circom │ ├── test_caret2_regex.circom │ ├── test_caret3_regex.circom │ ├── test_caret4_regex.circom │ ├── test_caret5_regex.circom │ ├── test_dollar1_regex.circom │ ├── test_dollar2_regex.circom │ ├── test_dot1_regex.circom │ ├── test_dot2_regex.circom │ ├── test_email_addr_regex.circom │ ├── test_email_domain_regex.circom │ ├── test_from_addr_regex.circom │ ├── test_international_chars_decomposed.circom │ ├── test_invitation_code_with_prefix_regex.circom │ ├── test_message_id_regex.circom │ ├── test_negate1_regex.circom │ ├── test_negate2_regex.circom │ ├── test_plus1_regex.circom │ ├── test_plus2_regex.circom │ ├── test_plus3_regex.circom │ ├── test_plus4_regex.circom │ ├── test_question1_regex.circom │ ├── test_question2_regex.circom │ ├── test_question3_regex.circom │ ├── test_reveal_check1_regex.circom │ ├── test_reveal_check2_regex.circom │ ├── test_simple_regex.circom │ ├── test_simple_regex_decomposed.circom │ ├── test_subject_all_regex.circom │ ├── test_timestamp_regex.circom │ └── test_to_addr_regex.circom │ ├── dollar.test.js │ ├── dot.test.js │ ├── email_addr.test.js │ ├── email_domain.test.js │ ├── from_addr.test.js │ ├── international_chars.test.js │ ├── invitation_code.test.js │ ├── message_id_regex.test.js │ ├── negate_regex.test.js │ ├── plus.test.js │ ├── question.test.js │ ├── reveal_check.test.js │ ├── simple_regex.test.js │ ├── simple_regex_decomposed.test.js │ ├── subject_all.test.js │ ├── timestamp.test.js │ └── to_addr.test.js └── compiler ├── Cargo.toml ├── README.md ├── package.json └── src ├── bin └── compiler.rs ├── circom.rs ├── dfa_tests.json ├── errors.rs ├── halo2.rs ├── lib.rs ├── regex.rs ├── structs.rs └── wasm.rs /.cargo/config: -------------------------------------------------------------------------------- 1 | paths = ["./"] 2 | 3 | [target.x86_64-unknown-linux-gnu] 4 | rustflags = ["-Clink-arg=-Wl,--allow-multiple-definition"] -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "es6": true 5 | }, 6 | "extends": ["eslint:recommended"], 7 | "overrides": [], 8 | "parserOptions": { 9 | "ecmaVersion": 2020, 10 | "sourceType": "script" 11 | }, 12 | "globals": { 13 | "process": "readonly", 14 | "__dirname": "readonly", 15 | "before": "readonly", 16 | "it": "readonly", 17 | "describe": "readonly", 18 | "BigInt": "readonly" 19 | }, 20 | "rules": { 21 | "indent": ["error", 4], 22 | "linebreak-style": ["error", "unix"], 23 | "quotes": ["error", "single"], 24 | "semi": ["error", "always"] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout code 10 | uses: actions/checkout@v2 11 | - name: Setup Node.js 12 | uses: actions/setup-node@v3 13 | with: 14 | node-version: 18 15 | - name: Setup Rust 16 | uses: actions-rs/toolchain@v1 17 | with: 18 | toolchain: stable 19 | override: true 20 | components: rustfmt, clippy 21 | - name: Install wasm-pack 22 | run: cargo install wasm-pack 23 | - name: Download circom v2.1.9 (Linux) 24 | run: wget https://github.com/iden3/circom/releases/download/v2.1.9/circom-linux-amd64 -O /usr/local/bin/circom && chmod +x /usr/local/bin/circom 25 | - name: Install yarn 26 | run: npm install -g yarn 27 | - name: Install dependencies 28 | run: yarn install --immutable 29 | - name: Install bun 30 | uses: oven-sh/setup-bun@v1 31 | with: 32 | bun-version: latest 33 | - name: Run tests 34 | run: yarn test 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Microbundle cache 51 | .rpt2_cache/ 52 | .rts2_cache_cjs/ 53 | .rts2_cache_es/ 54 | .rts2_cache_umd/ 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | .env.test 68 | 69 | # parcel-bundler cache (https://parceljs.org/) 70 | .cache 71 | 72 | # Next.js build output 73 | .next 74 | 75 | # Nuxt.js build / generate output 76 | .nuxt 77 | dist 78 | 79 | # Gatsby files 80 | .cache/ 81 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 82 | # https://nextjs.org/blog/next-9-1#public-directory-support 83 | # public 84 | 85 | # vuepress build output 86 | .vuepress/dist 87 | 88 | # Serverless directories 89 | .serverless/ 90 | 91 | # FuseBox cache 92 | .fusebox/ 93 | 94 | # DynamoDB Local files 95 | .dynamodb/ 96 | 97 | # TernJS port file 98 | .tern-port 99 | 100 | .vscode 101 | build/* 102 | !build/.placeholder 103 | 104 | target 105 | 106 | Cargo.lock 107 | 108 | .DS_Store 109 | packages/*/build 110 | 111 | package-lock.json 112 | yarn.lock 113 | 114 | index.node 115 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "useTabs": false, 4 | "semi": true, 5 | "singleQuote": true, 6 | "endOfLine": "lf", 7 | "trailingComma": "none", 8 | "bracketSpacing": true, 9 | "arrowParens": "avoid" 10 | } 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["packages/*"] 3 | exclude = ["packages/circom", "test"] 4 | 5 | # [patch."https://github.com/stalwartlabs/mail-builder"] 6 | # mail-builder = { version = "0.2.5", git = "https://github.com/stalwartlabs//mail-builder", tag = "0.2.5" } 7 | 8 | # [patch."https://github.com/stalwartlabs/mail-parser"] 9 | # mail-parser = { version = "0.8", git = "https://github.com/stalwartlabs//mail-parser", tag = "0.8.0" } 10 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ["@babel/preset-env", { targets: { node: "current" } }], 4 | ["@babel/preset-react", { runtime: "automatic" }], 5 | ["jest"], 6 | ], 7 | plugins: ["@babel/plugin-transform-modules-commonjs"], 8 | }; 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zk-email/zk-regex", 3 | "version": "2.3.2", 4 | "private": true, 5 | "description": "zk regex circuit for content attestation", 6 | "main": "pkg/zk_regex_compiler_bg.wasm", 7 | "workspaces": [ 8 | "packages/*" 9 | ], 10 | "contributors": [ 11 | "Sora Suegami ", 12 | "Yush G ", 13 | "Javier Su ", 14 | "Kata Choi ", 15 | "Aditya Bisht " 16 | ], 17 | "scripts": { 18 | "install": "yarn workspaces -pt run install", 19 | "build": "yarn workspaces -pt run build", 20 | "postinstall": "cargo install --path ./packages/compiler", 21 | "test": "yarn workspaces -pt run test", 22 | "upload-binary": "yarn workspaces -pt run upload-binary" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git+https://github.com/zk-email-verify/zk-regex.git" 27 | }, 28 | "keywords": [ 29 | "circom", 30 | "circuit", 31 | "regex", 32 | "zk", 33 | "attestation" 34 | ], 35 | "license": "MIT", 36 | "bugs": { 37 | "url": "https://github.com/zk-email-verify/zk-regex/issues" 38 | }, 39 | "homepage": "https://github.com/zk-email-verify/zk-regex#readme", 40 | "devDependencies": { 41 | "@babel/core": "^7.22.5", 42 | "@babel/plugin-transform-modules-commonjs": "^7.22.15", 43 | "@babel/preset-env": "^7.22.2", 44 | "@babel/preset-react": "^7.22.0", 45 | "@types/jest": "^29.5.4", 46 | "babel-jest": "^29.5.0", 47 | "babel-preset-jest": "^29.5.0", 48 | "jest": "^29.5.0", 49 | "prettier": "^3.0.0", 50 | "prettier-plugin-solidity": "^1.1.3" 51 | }, 52 | "engines": { 53 | "yarn": "^1.22.0" 54 | } 55 | } -------------------------------------------------------------------------------- /packages/apis/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | **/node_modules 3 | **/.DS_Store 4 | npm-debug.log* 5 | -------------------------------------------------------------------------------- /packages/apis/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zk-regex-apis" 3 | version = "2.3.2" 4 | license = "MIT" 5 | edition = "2018" 6 | authors = [ 7 | "Javier Su ", 8 | "Kata Choi ", 9 | "Sora Suegami ", 10 | "Yush G ", 11 | "Aditya Bisht ", 12 | ] 13 | 14 | [lib] 15 | crate-type = ["rlib", "cdylib"] 16 | 17 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 18 | 19 | [dependencies] 20 | # zk-regex-compiler = { path = "../compiler", default-features = false } 21 | serde = { version = "1.0.159", features = ["derive"] } 22 | fancy-regex = "0.13.0" 23 | itertools = "0.13.0" 24 | thiserror = "1.0.40" 25 | serde_json = "1.0.95" 26 | wasm-bindgen = "0.2" 27 | serde-wasm-bindgen = "0.6.5" 28 | js-sys = "0.3.69" 29 | wasm-bindgen-test = "0.3.42" 30 | console_error_panic_hook = "0.1.7" 31 | -------------------------------------------------------------------------------- /packages/apis/README.md: -------------------------------------------------------------------------------- 1 | # zk-regex-apis 2 | 3 | Helpful nodejs/Rust APIs in [zk-regex](https://github.com/zkemail/zk-regex/tree/main). 4 | 5 | ## Installing zk-regex-apis 6 | 7 | Installing zk-regex-apis requires a [supported version of Node and Rust](https://github.com/neon-bindings/neon#platform-support). 8 | 9 | You can install the project with npm. In the project directory, run: 10 | 11 | ```sh 12 | $ npm install 13 | ``` 14 | 15 | This fully installs the project, including installing any dependencies and running the build. 16 | 17 | ## Building zk-regex-apis 18 | 19 | If you have already installed the project and only want to run the build, run: 20 | 21 | ```sh 22 | $ npm run build 23 | ``` 24 | 25 | ## Compiling zk-regex-apis to wasm 26 | 27 | ### For web usage 28 | Install `wasm-pack` if not already installed 29 | 30 | ```sh 31 | cargo install wasm-pack 32 | ``` 33 | 34 | Compile the web package 35 | 36 | ```sh 37 | wasm-pack build --target nodejs 38 | ``` 39 | 40 | Pack the package (optional) 41 | 42 | ```sh 43 | wasm-pack build --target nodejs 44 | cd pkg 45 | npm pkg set type='module' 46 | wasm-pack pack 47 | ``` 48 | 49 | The output package file will be `packages/compiler/pkg/zk-regex-apis-1.1.1.tgz` 50 | 51 | ### For tests 52 | 53 | ```sh 54 | wasm-pack test --node 55 | ``` 56 | 57 | ## Available Scripts 58 | 59 | In the project directory, you can run: 60 | 61 | ### `npm install` 62 | 63 | Installs the project, including running `npm run build`. 64 | 65 | ### `npm build` 66 | 67 | Additional [`cargo build`](https://doc.rust-lang.org/cargo/commands/cargo-build.html) arguments may be passed to `npm build` and `npm build-*` commands. For example, to enable a [cargo feature](https://doc.rust-lang.org/cargo/reference/features.html): 68 | 69 | ``` 70 | npm run build -- --feature=beetle 71 | ``` 72 | 73 | #### `npm build-debug` 74 | 75 | Alias for `npm build`. 76 | 77 | #### `npm build-release` 78 | 79 | Same as [`npm build`](#npm-build) but, builds the module with the [`release`](https://doc.rust-lang.org/cargo/reference/profiles.html#release) profile. Release builds will compile slower, but run faster. 80 | 81 | ### `npm test` 82 | 83 | Runs the unit tests by calling `wasm-pack test --node`. 84 | 85 | ## Project Layout 86 | 87 | The directory structure of this project is: 88 | 89 | ``` 90 | zk-regex-compiler/ 91 | ├── Cargo.toml 92 | ├── README.md 93 | ├── package.json 94 | ├── src/ 95 | | └── lib.rs 96 | └── target/ 97 | ``` 98 | 99 | ### Cargo.toml 100 | 101 | The Cargo [manifest file](https://doc.rust-lang.org/cargo/reference/manifest.html), which informs the `cargo` command. 102 | 103 | ### README.md 104 | 105 | This file. 106 | 107 | ### package.json 108 | 109 | The npm [manifest file](https://docs.npmjs.com/cli/v7/configuring-npm/package-json), which informs the `npm` command. 110 | 111 | ### src/ 112 | 113 | The directory tree containing the Rust source code for the project. 114 | 115 | ### src/lib.rs 116 | 117 | The Rust library's main module. 118 | 119 | ### target/ 120 | 121 | Binary artifacts generated by the Rust build. 122 | -------------------------------------------------------------------------------- /packages/apis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zk-email/zk-regex-apis", 3 | "version": "2.3.2", 4 | "description": "apis compatible with [zk-regex](https://github.com/zkemail/zk-regex/tree/main).", 5 | "contributors": [ 6 | "Javier Su ", 7 | "Kata Choi ", 8 | "Sora Suegami ", 9 | "Yush G ", 10 | "Aditya Bisht " 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/zkemail/zk-regex.git" 15 | }, 16 | "scripts": { 17 | "build": "wasm-pack build --target nodejs --out-dir ./pkg/", 18 | "build-debug": "npm run build --", 19 | "build-release": "npm run build --", 20 | "install": "npm run build-debug", 21 | "install-release": "npm run build-release", 22 | "test": "cargo test && wasm-pack test --node && bun test", 23 | "test-js": "jest", 24 | "upload-binary": "wasm-pack publish -t nodejs" 25 | }, 26 | "license": "MIT", 27 | "devDependencies": { 28 | "@types/jest": "^29.5.13", 29 | "jest": "^29.7.0" 30 | } 31 | } -------------------------------------------------------------------------------- /packages/apis/src/decomposed_defs/body_hash.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "(\r\n|^)dkim-signature:" 6 | }, 7 | { 8 | "is_public": false, 9 | "regex_def": "([a-z]+=[^;]+; )+bh=" 10 | }, 11 | { 12 | "is_public": true, 13 | "regex_def": "[a-zA-Z0-9+/=]+" 14 | }, 15 | { 16 | "is_public": false, 17 | "regex_def": ";" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/apis/src/decomposed_defs/email_addr.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": true, 5 | "regex_def": "[A-Za-z0-9!#$%&'*+=?\\-\\^_`{|}~./@]+@[A-Za-z0-9.\\-]+" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /packages/apis/src/decomposed_defs/email_domain.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "[A-Za-z0-9!#$%&'*+=?\\-\\^_`{|}~./]+@" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "[A-Za-z0-9.\\-@]+" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /packages/apis/src/decomposed_defs/from_addr.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "(\r\n|^)from:" 6 | }, 7 | { 8 | "is_public": false, 9 | "regex_def": "([^\r\n]+<)?" 10 | }, 11 | { 12 | "is_public": true, 13 | "regex_def": "[^<>]+" 14 | }, 15 | { 16 | "is_public": false, 17 | "regex_def": ">?\r\n" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /packages/apis/src/decomposed_defs/from_all.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "(\r\n|^)from:" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "[^\r\n]+" 10 | }, 11 | { 12 | "is_public": false, 13 | "regex_def": "\r\n" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /packages/apis/src/decomposed_defs/message_id.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "(\r\n|^)message-id:" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "<[A-Za-z0-9=@\\.\\+_-]+>" 10 | }, 11 | { 12 | "is_public": false, 13 | "regex_def": "\r\n" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/apis/src/decomposed_defs/subject_all.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "(\r\n|^)subject:" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "[^\r\n]+" 10 | }, 11 | { 12 | "is_public": false, 13 | "regex_def": "\r\n" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/apis/src/decomposed_defs/timestamp.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "(\r\n|^)dkim-signature:" 6 | }, 7 | { 8 | "is_public": false, 9 | "regex_def": "([a-z]+=[^;]+; )+t=" 10 | }, 11 | { 12 | "is_public": true, 13 | "regex_def": "[0-9]+" 14 | }, 15 | { 16 | "is_public": false, 17 | "regex_def": ";" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/apis/src/decomposed_defs/to_addr.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "(\r\n|^)to:" 6 | }, 7 | { 8 | "is_public": false, 9 | "regex_def": "([^\r\n]+<)?" 10 | }, 11 | { 12 | "is_public": true, 13 | "regex_def": "[^<>]+" 14 | }, 15 | { 16 | "is_public": false, 17 | "regex_def": ">?\r\n" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /packages/apis/src/decomposed_defs/to_all.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "(\r\n|^)to:" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "[^\r\n]+" 10 | }, 11 | { 12 | "is_public": false, 13 | "regex_def": "\r\n" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /packages/apis/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod extract_substrs; 2 | pub mod padding; 3 | #[cfg(target_arch = "wasm32")] 4 | mod wasm; 5 | #[cfg(target_arch = "wasm32")] 6 | pub use crate::wasm::*; 7 | -------------------------------------------------------------------------------- /packages/apis/src/padding.rs: -------------------------------------------------------------------------------- 1 | pub fn pad_string(str: &str, padded_bytes_size: usize) -> Vec { 2 | let mut padded_bytes = str.as_bytes().to_vec(); 3 | padded_bytes.append(&mut vec![0; padded_bytes_size - padded_bytes.len()]); 4 | padded_bytes 5 | } 6 | -------------------------------------------------------------------------------- /packages/apis/src/wasm.rs: -------------------------------------------------------------------------------- 1 | use crate::extract_substrs::*; 2 | use crate::*; 3 | use console_error_panic_hook; 4 | use js_sys::Array; 5 | use serde_json::Value; 6 | use std::panic; 7 | use wasm_bindgen::prelude::*; 8 | 9 | #[wasm_bindgen] 10 | #[allow(non_snake_case)] 11 | pub fn padString(str: &str, paddedBytesSize: usize) -> Array { 12 | let padded_bytes = padding::pad_string(str, paddedBytesSize); 13 | 14 | let arr = Array::new_with_length(padded_bytes.len() as u32); 15 | for (i, byte) in padded_bytes.iter().enumerate() { 16 | arr.set(i as u32, JsValue::from(*byte)); 17 | } 18 | 19 | arr 20 | } 21 | 22 | #[wasm_bindgen] 23 | #[allow(non_snake_case)] 24 | pub fn extractSubstrIdxes( 25 | inputStr: &str, 26 | regexConfig: JsValue, 27 | reveal_private: bool, 28 | ) -> Result { 29 | let regex_config = parse_js_regex_config(regexConfig)?; 30 | 31 | let idxes = extract_substrs::extract_substr_idxes(inputStr, ®ex_config, reveal_private) 32 | .map_err(|e| { 33 | println!("e: {:?}", e); 34 | let error_msg = format!("Failed to extract indxes: {}", e); 35 | JsValue::from_str(&error_msg) 36 | })?; 37 | 38 | let arr = Array::new_with_length(idxes.len() as u32); 39 | for (i, idx) in idxes.iter().enumerate() { 40 | let js_arr = Array::new_with_length(2); 41 | js_arr.set(0, JsValue::from(idx.0 as u32)); 42 | js_arr.set(1, JsValue::from(idx.1 as u32)); 43 | arr.set(i as u32, JsValue::from(js_arr)); 44 | } 45 | 46 | Ok(arr) 47 | } 48 | 49 | #[wasm_bindgen] 50 | #[allow(non_snake_case)] 51 | pub fn extractSubstr( 52 | inputStr: &str, 53 | regexConfig: JsValue, 54 | reveal_private: bool, 55 | ) -> Result { 56 | let regex_config = parse_js_regex_config(regexConfig)?; 57 | 58 | let result_strs = extract_substrs::extract_substr(inputStr, ®ex_config, reveal_private) 59 | .map_err(|e| { 60 | println!("e: {:?}", e); 61 | let error_msg = format!("Failed to extract strings: {}", e); 62 | JsValue::from_str(&error_msg) 63 | })?; 64 | 65 | let js_array = Array::new_with_length(result_strs.len() as u32); 66 | for (i, s) in result_strs.into_iter().enumerate() { 67 | js_array.set(i as u32, JsValue::from_str(&s)); 68 | } 69 | 70 | Ok(js_array) 71 | } 72 | 73 | #[wasm_bindgen] 74 | #[allow(non_snake_case)] 75 | pub fn extractEmailAddrIdxes(inputStr: &str) -> Result { 76 | let regex_config = include_str!("./decomposed_defs/email_addr.json"); 77 | extractSubstrIdxes(inputStr, JsValue::from_str(regex_config), false) 78 | } 79 | 80 | #[wasm_bindgen] 81 | #[allow(non_snake_case)] 82 | pub fn extractEmailDomainIdxes(inputStr: &str) -> Result { 83 | let regex_config = include_str!("./decomposed_defs/email_domain.json"); 84 | extractSubstrIdxes(inputStr, JsValue::from_str(regex_config), false) 85 | } 86 | 87 | // #[wasm_bindgen] 88 | // #[allow(non_snake_case)] 89 | // pub fn extractEmailAddrWithNameIdxes(inputStr: &str) -> Array { 90 | // let regex_config = include_str!("./decomposed_defs/email_addr_with_name.json"); 91 | // extractSubstrIdxes(inputStr, JsValue::from_str(regex_config)) 92 | // } 93 | 94 | #[wasm_bindgen] 95 | #[allow(non_snake_case)] 96 | pub fn extractFromAllIdxes(inputStr: &str) -> Result { 97 | let regex_config = include_str!("./decomposed_defs/from_all.json"); 98 | extractSubstrIdxes(inputStr, JsValue::from_str(regex_config), false) 99 | } 100 | 101 | #[wasm_bindgen] 102 | #[allow(non_snake_case)] 103 | pub fn extractFromAddrIdxes(inputStr: &str) -> Result { 104 | let regex_config = include_str!("./decomposed_defs/from_addr.json"); 105 | extractSubstrIdxes(inputStr, JsValue::from_str(regex_config), false) 106 | } 107 | 108 | #[wasm_bindgen] 109 | #[allow(non_snake_case)] 110 | pub fn extractToAllIdxes(inputStr: &str) -> Result { 111 | let regex_config = include_str!("./decomposed_defs/to_all.json"); 112 | extractSubstrIdxes(inputStr, JsValue::from_str(regex_config), false) 113 | } 114 | 115 | #[wasm_bindgen] 116 | #[allow(non_snake_case)] 117 | pub fn extractToAddrIdxes(inputStr: &str) -> Result { 118 | let regex_config = include_str!("./decomposed_defs/to_addr.json"); 119 | extractSubstrIdxes(inputStr, JsValue::from_str(regex_config), false) 120 | } 121 | 122 | #[wasm_bindgen] 123 | #[allow(non_snake_case)] 124 | pub fn extractSubjectAllIdxes(inputStr: &str) -> Result { 125 | let regex_config = include_str!("./decomposed_defs/subject_all.json"); 126 | extractSubstrIdxes(inputStr, JsValue::from_str(regex_config), false) 127 | } 128 | 129 | #[wasm_bindgen] 130 | #[allow(non_snake_case)] 131 | pub fn extractBodyHashIdxes(inputStr: &str) -> Result { 132 | let regex_config = include_str!("./decomposed_defs/body_hash.json"); 133 | extractSubstrIdxes(inputStr, JsValue::from_str(regex_config), false) 134 | } 135 | 136 | #[wasm_bindgen] 137 | #[allow(non_snake_case)] 138 | pub fn extractTimestampIdxes(inputStr: &str) -> Result { 139 | let regex_config = include_str!("./decomposed_defs/timestamp.json"); 140 | extractSubstrIdxes(inputStr, JsValue::from_str(regex_config), false) 141 | } 142 | 143 | #[wasm_bindgen] 144 | #[allow(non_snake_case)] 145 | pub fn extractMessageIdIdxes(inputStr: &str) -> Result { 146 | let regex_config = include_str!("./decomposed_defs/message_id.json"); 147 | extractSubstrIdxes(inputStr, JsValue::from_str(regex_config), false) 148 | } 149 | 150 | // Accepts regexConfig either as string or js object 151 | fn parse_js_regex_config(regex_config: JsValue) -> Result { 152 | // Checks if regexConfig is passed as string or object 153 | // As string 154 | let parsed_config: DecomposedRegexConfig = if regex_config.is_string() { 155 | let config_str = regex_config.as_string().unwrap(); 156 | serde_json::from_str(&config_str).map_err(|e| { 157 | let error_msg = format!("Failed to parse JSON string: {}", e); 158 | JsValue::from_str(&error_msg) 159 | })? 160 | // As object 161 | } else { 162 | serde_wasm_bindgen::from_value(regex_config).map_err(|e| { 163 | let error_msg = simplify_error(&e); 164 | JsValue::from_str(&error_msg) 165 | })? 166 | }; 167 | 168 | Ok(parsed_config) 169 | } 170 | 171 | fn simplify_error(e: &serde_wasm_bindgen::Error) -> String { 172 | let error_string = e.to_string(); 173 | if let Some(json_error) = serde_json::from_str::(&error_string).ok() { 174 | if let Some(message) = json_error["message"].as_str() { 175 | return message.to_string(); 176 | } 177 | } 178 | error_string 179 | } 180 | -------------------------------------------------------------------------------- /packages/apis/tests/extract_substr.test.js: -------------------------------------------------------------------------------- 1 | import { extractSubstrIdxes, extractSubstr } from '../pkg/zk_regex_apis'; 2 | import airbnbEml from './airbnb_eml'; 3 | 4 | describe('Extract substr test suite', async () => { 5 | // Wait for wasm to initialize 6 | await new Promise(r => setTimeout(r, 300)); 7 | test('Should extract indicies from object input', () => { 8 | const parts = { 9 | parts: [ 10 | { 11 | isPublic: true, 12 | regexDef: 'Hello' 13 | } 14 | ] 15 | }; 16 | const result = extractSubstrIdxes(airbnbEml, parts, false); 17 | expect(result.length).toBe(1); 18 | expect(result[0].length).toBe(2); 19 | }); 20 | 21 | test('Should extract indicies from object input, hide private', () => { 22 | const parts = { 23 | parts: [ 24 | { 25 | isPublic: true, 26 | regexDef: 'Hello ' 27 | }, 28 | { 29 | isPublic: false, 30 | regexDef: 'guys!' 31 | } 32 | ] 33 | }; 34 | const result = extractSubstrIdxes(airbnbEml, parts, false); 35 | expect(result.length).toBe(1); 36 | expect(result[0].length).toBe(2); 37 | }); 38 | 39 | test('Should extract indicies from object input, reveal private', () => { 40 | const parts = { 41 | parts: [ 42 | { 43 | isPublic: true, 44 | regexDef: 'Hello ' 45 | }, 46 | { 47 | isPublic: false, 48 | regexDef: 'guys!' 49 | } 50 | ] 51 | }; 52 | const result = extractSubstrIdxes(airbnbEml, parts, true); 53 | expect(result.length).toBe(2); 54 | expect(result[0].length).toBe(2); 55 | expect(result[1].length).toBe(2); 56 | }); 57 | 58 | test('Should extract indicies from stringified input', () => { 59 | const parts = { 60 | parts: [ 61 | { 62 | isPublic: false, 63 | regexDef: 'Hello' 64 | } 65 | ] 66 | }; 67 | const result = extractSubstrIdxes( 68 | airbnbEml, 69 | JSON.stringify(parts), 70 | true 71 | ); 72 | expect(result.length).toBe(1); 73 | expect(result[0].length).toBe(2); 74 | }); 75 | 76 | test('Should throw helpful js error on wrong object input', () => { 77 | const parts = { 78 | wrong: 'input' 79 | }; 80 | try { 81 | extractSubstrIdxes(airbnbEml, parts, false); 82 | } catch (err) { 83 | expect(err).toBe('Error: missing field `parts`'); 84 | return; 85 | } 86 | throw new Error('Did not catch wrong input'); 87 | }); 88 | 89 | test('Should throw helpful js error on wrong stringified input', () => { 90 | const parts = { 91 | wrong: 'input' 92 | }; 93 | try { 94 | extractSubstrIdxes(airbnbEml, JSON.stringify(parts), false); 95 | } catch (err) { 96 | const includesErr = err.includes( 97 | 'Failed to parse JSON string: missing field `parts`' 98 | ); 99 | expect(includesErr).toBe(true); 100 | return; 101 | } 102 | throw new Error('Did not catch wrong input'); 103 | }); 104 | 105 | test('Should throw helpful js error on wrong object input 2', () => { 106 | const parts = { 107 | parts: [ 108 | { 109 | isPublic: false 110 | } 111 | ] 112 | }; 113 | try { 114 | extractSubstrIdxes(airbnbEml, parts, false); 115 | } catch (err) { 116 | expect(err).toBe('Error: missing field `regex_def`'); 117 | return; 118 | } 119 | throw new Error('Did not catch wrong input'); 120 | }); 121 | 122 | test('Should throw helpful js error on no found result', () => { 123 | const parts = { 124 | parts: [ 125 | { 126 | isPublic: true, 127 | regexDef: 'Hello' 128 | }, 129 | { 130 | isPublic: false, 131 | regexDef: 'yall!' 132 | } 133 | ] 134 | }; 135 | try { 136 | extractSubstrIdxes(airbnbEml, parts, false); 137 | } catch (err) { 138 | const includes = err.includes( 139 | 'Failed to extract indxes: Substring of the entire regex (Hello)(yall!) is not found given input_str' 140 | ); 141 | expect(includes).toBe(true); 142 | return; 143 | } 144 | throw new Error('Did not throw an error'); 145 | }); 146 | 147 | test('extractSubstr should return actual matched string', () => { 148 | const parts = { 149 | parts: [ 150 | { 151 | isPublic: true, 152 | regexDef: 'Hello' 153 | } 154 | ] 155 | }; 156 | const strs = extractSubstr(airbnbEml, parts, false); 157 | expect(strs[0]).toBe('Hello'); 158 | }); 159 | 160 | test('extractSubstr should return an empty array on all private fields', () => { 161 | const parts = { 162 | parts: [ 163 | { 164 | isPublic: false, 165 | regexDef: 'Hello' 166 | } 167 | ] 168 | }; 169 | const strs = extractSubstr(airbnbEml, parts, false); 170 | expect(strs.length).toBe(0); 171 | }); 172 | }); 173 | -------------------------------------------------------------------------------- /packages/circom/README.md: -------------------------------------------------------------------------------- 1 | # zk-regex-circom 2 | 3 | Circom circuits for regex verification in [zk-regex](https://github.com/zkemail/zk-regex/tree/main). 4 | This package contains circom circuits and decomposed regex definitions for common regexes in `./circuits/common` folder. 5 | 6 | ## Note 7 | Our `email_domain_regex.circom` circuit cannot capture an email address that contains "@" in the name part before the domain part, e.g., "alice@gmail.com@dummy.com", due to limitation of our circuit construction. For example, when "alice@gmail.com@dummy.com" is given, that circuit outputs not "dummy.com" but "gmail.com@dummy.com" as an exposed substring for the domain. However, an adversary cannot exploit this feature to expose a fake domain since the true domain at the end will also be revealed along with it. -------------------------------------------------------------------------------- /packages/circom/circuits/common/body_hash.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "(\r\n|^)dkim-signature:" 6 | }, 7 | { 8 | "is_public": false, 9 | "regex_def": "([a-z]+=[^;]+; )+bh=" 10 | }, 11 | { 12 | "is_public": true, 13 | "regex_def": "[a-zA-Z0-9+/=]+" 14 | }, 15 | { 16 | "is_public": false, 17 | "regex_def": ";" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/circom/circuits/common/email_addr.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": true, 5 | "regex_def": "[A-Za-z0-9!#$%&'*+=?\\-\\^_`{|}~./@]+@[A-Za-z0-9.\\-]+" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /packages/circom/circuits/common/email_addr_with_name_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | include "./reversed_bracket_regex.circom"; 5 | 6 | template EmailAddrWithNameRegex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | signal output reveal0[msg_bytes]; 10 | 11 | signal reversed_msg[msg_bytes]; 12 | signal reversed_reveal0[msg_bytes]; 13 | for(var i=0; i" 10 | }, 11 | { 12 | "is_public": false, 13 | "regex_def": "\r\n" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/circom/circuits/common/reversed_bracket.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": ">" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "[^<>]+" 10 | }, 11 | { 12 | "is_public": false, 13 | "regex_def": "<.*" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /packages/circom/circuits/common/subject_all.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "(\r\n|^)subject:" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "[^\r\n]+" 10 | }, 11 | { 12 | "is_public": false, 13 | "regex_def": "\r\n" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/circom/circuits/common/timestamp.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "(\r\n|^)dkim-signature:" 6 | }, 7 | { 8 | "is_public": false, 9 | "regex_def": "([a-z]+=[^;]+; )+t=" 10 | }, 11 | { 12 | "is_public": true, 13 | "regex_def": "[0-9]+" 14 | }, 15 | { 16 | "is_public": false, 17 | "regex_def": ";" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/circom/circuits/common/to_addr_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | include "@zk-email/zk-regex-circom/circuits/common/to_all_regex.circom"; 5 | include "@zk-email/zk-regex-circom/circuits/common/email_addr_regex.circom"; 6 | include "@zk-email/zk-regex-circom/circuits/common/email_addr_with_name_regex.circom"; 7 | 8 | 9 | template ToAddrRegex(msg_bytes) { 10 | signal input msg[msg_bytes]; 11 | signal output out; 12 | signal output reveal0[msg_bytes]; 13 | 14 | signal toOut; 15 | signal toReveal[msg_bytes]; 16 | (toOut, toReveal) <== ToAllRegex(msg_bytes)(msg); 17 | toOut === 1; 18 | 19 | signal emailNameOut; 20 | signal emailNameReveal[msg_bytes]; 21 | (emailNameOut, emailNameReveal) <== EmailAddrWithNameRegex(msg_bytes)(toReveal); 22 | 23 | signal emailAddrOut; 24 | signal emailAddrReveal[msg_bytes]; 25 | (emailAddrOut, emailAddrReveal) <== EmailAddrRegex(msg_bytes)(toReveal); 26 | 27 | out <== MultiOR(2)([emailNameOut, emailAddrOut]); 28 | for(var i=0; i", 8 | "Kata Choi ", 9 | "Sora Suegami ", 10 | "Yush G ", 11 | "Aditya Bisht " 12 | ], 13 | "scripts": { 14 | "test": "jest", 15 | "install": "echo", 16 | "build": "echo", 17 | "upload-binary": "echo" 18 | }, 19 | "dependencies": { 20 | "commander": "^11.0.0", 21 | "snarkjs": "^0.7.5" 22 | }, 23 | "devDependencies": { 24 | "@types/jest": "^29.5.4", 25 | "chai": "^4.3.7", 26 | "circom_tester": "^0.0.20", 27 | "circomlib": "^2.0.5", 28 | "circomlibjs": "^0.1.2", 29 | "ffjavascript": "^0.3.1", 30 | "jest": "^29.5.0", 31 | "mocha": "^10.2.0" 32 | }, 33 | "babel": { 34 | "presets": [ 35 | [ 36 | "@babel/preset-env" 37 | ] 38 | ] 39 | } 40 | } -------------------------------------------------------------------------------- /packages/circom/tests/body_hash_regex.test.js: -------------------------------------------------------------------------------- 1 | import circom_tester from 'circom_tester'; 2 | import * as path from 'path'; 3 | import { readFileSync, writeFileSync } from 'fs'; 4 | import apis from '../../apis/pkg'; 5 | import compiler from '../../compiler/pkg'; 6 | const option = { 7 | include: path.join(__dirname, '../../../node_modules') 8 | }; 9 | const wasm_tester = circom_tester.wasm; 10 | 11 | jest.setTimeout(600000); 12 | describe('Bodyhash Regex', () => { 13 | let circuit; 14 | beforeAll(async () => { 15 | const email_addr_json = readFileSync( 16 | path.join(__dirname, '../circuits/common/body_hash.json'), 17 | 'utf8' 18 | ); 19 | const circom = compiler.genFromDecomposed( 20 | email_addr_json, 21 | 'BodyHashRegex' 22 | ); 23 | writeFileSync( 24 | path.join(__dirname, '../circuits/common/body_hash_regex.circom'), 25 | circom 26 | ); 27 | 28 | circuit = await wasm_tester( 29 | path.join(__dirname, './circuits/test_body_hash_regex.circom'), 30 | option 31 | ); 32 | }); 33 | 34 | it('bodyhash in the header', async () => { 35 | const signatureField = `dkim-signature:v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1694989812; x=1695594612; dara=google.com; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=BWETwQ9JDReS4GyR2v2TTR8Bpzj9ayumsWQJ3q7vehs=; b=`; 36 | const paddedStr = apis.padString(signatureField, 1024); 37 | const circuitInputs = { 38 | msg: paddedStr 39 | }; 40 | const witness = await circuit.calculateWitness(circuitInputs); 41 | await circuit.checkConstraints(witness); 42 | expect(1n).toEqual(witness[1]); 43 | const prefixIdxes = apis.extractSubstrIdxes( 44 | signatureField, 45 | readFileSync( 46 | path.join(__dirname, '../circuits/common/body_hash.json'), 47 | 'utf8' 48 | ), 49 | false 50 | )[0]; 51 | for (let idx = 0; idx < 1024; ++idx) { 52 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 53 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 54 | } else { 55 | expect(0n).toEqual(witness[2 + idx]); 56 | } 57 | } 58 | }); 59 | 60 | it('bodyhash after new line', async () => { 61 | const signatureField = `\r\ndkim-signature:v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1694989812; x=1695594612; dara=google.com; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=BWETwQ9JDReS4GyR2v2TTR8Bpzj9ayumsWQJ3q7vehs=; b=`; 62 | const paddedStr = apis.padString(signatureField, 1024); 63 | const circuitInputs = { 64 | msg: paddedStr 65 | }; 66 | const witness = await circuit.calculateWitness(circuitInputs); 67 | await circuit.checkConstraints(witness); 68 | expect(1n).toEqual(witness[1]); 69 | const prefixIdxes = apis.extractSubstrIdxes( 70 | signatureField, 71 | readFileSync( 72 | path.join(__dirname, '../circuits/common/body_hash.json'), 73 | 'utf8' 74 | ), 75 | false 76 | )[0]; 77 | for (let idx = 0; idx < 1024; ++idx) { 78 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 79 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 80 | } else { 81 | expect(0n).toEqual(witness[2 + idx]); 82 | } 83 | } 84 | }); 85 | 86 | it('bodyhash in the invalid field', async () => { 87 | const signatureField = `\r\nto: dkim-signature:v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1694989812; x=1695594612; dara=google.com; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=BWETwQ9JDReS4GyR2v2TTR8Bpzj9ayumsWQJ3q7vehs=; b=`; 88 | const paddedStr = apis.padString(signatureField, 1024); 89 | const circuitInputs = { 90 | msg: paddedStr 91 | }; 92 | const witness = await circuit.calculateWitness(circuitInputs); 93 | await circuit.checkConstraints(witness); 94 | expect(0n).toEqual(witness[1]); 95 | for (let idx = 0; idx < 1024; ++idx) { 96 | expect(0n).toEqual(witness[2 + idx]); 97 | } 98 | }); 99 | 100 | it('invalid bodyhash with 255', async () => { 101 | const signatureField = `dkim-signature:v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1694989812; x=1695594612; dara=google.com; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=BWETwQ9JDReS4GyR2v2TTR8Bpzj9ayumsWQJ3q7vehs=; b=`; 102 | let paddedStr = apis.padString(signatureField, 1022); 103 | paddedStr.unshift(49); 104 | paddedStr.unshift(255); 105 | const circuitInputs = { 106 | msg: paddedStr 107 | }; 108 | async function failFn() { 109 | const witness = await circuit.calculateWitness(circuitInputs); 110 | await circuit.checkConstraints(witness); 111 | } 112 | await expect(failFn).rejects.toThrow(); 113 | }); 114 | }); 115 | -------------------------------------------------------------------------------- /packages/circom/tests/circuits/asterisk1.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "x" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "a*" 10 | }, 11 | { 12 | "is_public": false, 13 | "regex_def": "b" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/asterisk1_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: xa*b 6 | template Asterisk1Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[3][num_bytes]; 21 | component and[3][num_bytes]; 22 | signal states[num_bytes+1][3]; 23 | signal states_tmp[num_bytes+1][3]; 24 | signal from_zero_enabled[num_bytes+1]; 25 | from_zero_enabled[num_bytes] <== 0; 26 | component state_changed[num_bytes]; 27 | 28 | for (var i = 1; i < 3; i++) { 29 | states[0][i] <== 0; 30 | } 31 | 32 | for (var i = 0; i < num_bytes; i++) { 33 | state_changed[i] = MultiOR(2); 34 | states[i][0] <== 1; 35 | eq[0][i] = IsEqual(); 36 | eq[0][i].in[0] <== in[i]; 37 | eq[0][i].in[1] <== 120; 38 | and[0][i] = AND(); 39 | and[0][i].a <== states[i][0]; 40 | and[0][i].b <== eq[0][i].out; 41 | eq[1][i] = IsEqual(); 42 | eq[1][i].in[0] <== in[i]; 43 | eq[1][i].in[1] <== 97; 44 | and[1][i] = AND(); 45 | and[1][i].a <== states[i][1]; 46 | and[1][i].b <== eq[1][i].out; 47 | states_tmp[i+1][1] <== and[1][i].out; 48 | eq[2][i] = IsEqual(); 49 | eq[2][i].in[0] <== in[i]; 50 | eq[2][i].in[1] <== 98; 51 | and[2][i] = AND(); 52 | and[2][i].a <== states[i][1]; 53 | and[2][i].b <== eq[2][i].out; 54 | states[i+1][2] <== and[2][i].out; 55 | from_zero_enabled[i] <== MultiNOR(2)([states_tmp[i+1][1], states[i+1][2]]); 56 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 57 | state_changed[i].in[0] <== states[i+1][1]; 58 | state_changed[i].in[1] <== states[i+1][2]; 59 | } 60 | 61 | component is_accepted = MultiOR(num_bytes+1); 62 | for (var i = 0; i <= num_bytes; i++) { 63 | is_accepted.in[i] <== states[i][2]; 64 | } 65 | out <== is_accepted.out; 66 | signal is_consecutive[msg_bytes+1][3]; 67 | is_consecutive[msg_bytes][2] <== 0; 68 | for (var i = 0; i < msg_bytes; i++) { 69 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][2] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 70 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 71 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][2], is_consecutive[msg_bytes-1-i][1]]); 72 | } 73 | // substrings calculated: [{(1, 1)}] 74 | signal prev_states0[1][msg_bytes]; 75 | signal is_substr0[msg_bytes]; 76 | signal is_reveal0[msg_bytes]; 77 | signal output reveal0[msg_bytes]; 78 | for (var i = 0; i < msg_bytes; i++) { 79 | // the 0-th substring transitions: [(1, 1)] 80 | prev_states0[0][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][1]; 81 | is_substr0[i] <== MultiOR(1)([prev_states0[0][i] * states[i+2][1]]); 82 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 83 | reveal0[i] <== in[i+1] * is_reveal0[i]; 84 | } 85 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/asterisk2.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "a" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "b*" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/asterisk2_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: ab* 6 | template Asterisk2Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[2][num_bytes]; 21 | component and[2][num_bytes]; 22 | signal states[num_bytes+1][2]; 23 | signal states_tmp[num_bytes+1][2]; 24 | signal from_zero_enabled[num_bytes+1]; 25 | from_zero_enabled[num_bytes] <== 0; 26 | component state_changed[num_bytes]; 27 | 28 | for (var i = 1; i < 2; i++) { 29 | states[0][i] <== 0; 30 | } 31 | 32 | for (var i = 0; i < num_bytes; i++) { 33 | state_changed[i] = MultiOR(1); 34 | states[i][0] <== 1; 35 | eq[0][i] = IsEqual(); 36 | eq[0][i].in[0] <== in[i]; 37 | eq[0][i].in[1] <== 97; 38 | and[0][i] = AND(); 39 | and[0][i].a <== states[i][0]; 40 | and[0][i].b <== eq[0][i].out; 41 | eq[1][i] = IsEqual(); 42 | eq[1][i].in[0] <== in[i]; 43 | eq[1][i].in[1] <== 98; 44 | and[1][i] = AND(); 45 | and[1][i].a <== states[i][1]; 46 | and[1][i].b <== eq[1][i].out; 47 | states_tmp[i+1][1] <== and[1][i].out; 48 | from_zero_enabled[i] <== MultiNOR(1)([states_tmp[i+1][1]]); 49 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 50 | state_changed[i].in[0] <== states[i+1][1]; 51 | } 52 | 53 | component is_accepted = MultiOR(num_bytes+1); 54 | for (var i = 0; i <= num_bytes; i++) { 55 | is_accepted.in[i] <== states[i][1]; 56 | } 57 | out <== is_accepted.out; 58 | signal is_consecutive[msg_bytes+1][3]; 59 | is_consecutive[msg_bytes][2] <== 0; 60 | for (var i = 0; i < msg_bytes; i++) { 61 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][1] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 62 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 63 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][1], is_consecutive[msg_bytes-1-i][1]]); 64 | } 65 | // substrings calculated: [{(1, 1)}] 66 | signal prev_states0[1][msg_bytes]; 67 | signal is_substr0[msg_bytes]; 68 | signal is_reveal0[msg_bytes]; 69 | signal output reveal0[msg_bytes]; 70 | for (var i = 0; i < msg_bytes; i++) { 71 | // the 0-th substring transitions: [(1, 1)] 72 | prev_states0[0][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][1]; 73 | is_substr0[i] <== MultiOR(1)([prev_states0[0][i] * states[i+2][1]]); 74 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 75 | reveal0[i] <== in[i+1] * is_reveal0[i]; 76 | } 77 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/asterisk3.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "a" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "(x|y)*" 10 | }, 11 | { 12 | "is_public": false, 13 | "regex_def": "b" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/asterisk3_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: a(x|y)*b 6 | template Asterisk3Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[4][num_bytes]; 21 | component and[3][num_bytes]; 22 | component multi_or[1][num_bytes]; 23 | signal states[num_bytes+1][3]; 24 | signal states_tmp[num_bytes+1][3]; 25 | signal from_zero_enabled[num_bytes+1]; 26 | from_zero_enabled[num_bytes] <== 0; 27 | component state_changed[num_bytes]; 28 | 29 | for (var i = 1; i < 3; i++) { 30 | states[0][i] <== 0; 31 | } 32 | 33 | for (var i = 0; i < num_bytes; i++) { 34 | state_changed[i] = MultiOR(2); 35 | states[i][0] <== 1; 36 | eq[0][i] = IsEqual(); 37 | eq[0][i].in[0] <== in[i]; 38 | eq[0][i].in[1] <== 97; 39 | and[0][i] = AND(); 40 | and[0][i].a <== states[i][0]; 41 | and[0][i].b <== eq[0][i].out; 42 | eq[1][i] = IsEqual(); 43 | eq[1][i].in[0] <== in[i]; 44 | eq[1][i].in[1] <== 120; 45 | eq[2][i] = IsEqual(); 46 | eq[2][i].in[0] <== in[i]; 47 | eq[2][i].in[1] <== 121; 48 | and[1][i] = AND(); 49 | and[1][i].a <== states[i][1]; 50 | multi_or[0][i] = MultiOR(2); 51 | multi_or[0][i].in[0] <== eq[1][i].out; 52 | multi_or[0][i].in[1] <== eq[2][i].out; 53 | and[1][i].b <== multi_or[0][i].out; 54 | states_tmp[i+1][1] <== and[1][i].out; 55 | eq[3][i] = IsEqual(); 56 | eq[3][i].in[0] <== in[i]; 57 | eq[3][i].in[1] <== 98; 58 | and[2][i] = AND(); 59 | and[2][i].a <== states[i][1]; 60 | and[2][i].b <== eq[3][i].out; 61 | states[i+1][2] <== and[2][i].out; 62 | from_zero_enabled[i] <== MultiNOR(2)([states_tmp[i+1][1], states[i+1][2]]); 63 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 64 | state_changed[i].in[0] <== states[i+1][1]; 65 | state_changed[i].in[1] <== states[i+1][2]; 66 | } 67 | 68 | component is_accepted = MultiOR(num_bytes+1); 69 | for (var i = 0; i <= num_bytes; i++) { 70 | is_accepted.in[i] <== states[i][2]; 71 | } 72 | out <== is_accepted.out; 73 | signal is_consecutive[msg_bytes+1][3]; 74 | is_consecutive[msg_bytes][2] <== 0; 75 | for (var i = 0; i < msg_bytes; i++) { 76 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][2] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 77 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 78 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][2], is_consecutive[msg_bytes-1-i][1]]); 79 | } 80 | // substrings calculated: [{(1, 1)}] 81 | signal prev_states0[1][msg_bytes]; 82 | signal is_substr0[msg_bytes]; 83 | signal is_reveal0[msg_bytes]; 84 | signal output reveal0[msg_bytes]; 85 | for (var i = 0; i < msg_bytes; i++) { 86 | // the 0-th substring transitions: [(1, 1)] 87 | prev_states0[0][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][1]; 88 | is_substr0[i] <== MultiOR(1)([prev_states0[0][i] * states[i+2][1]]); 89 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 90 | reveal0[i] <== in[i+1] * is_reveal0[i]; 91 | } 92 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/caret1.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": true, 5 | "regex_def": "^a" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/caret1_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: ^a 6 | template Caret1Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[2][num_bytes]; 21 | component and[2][num_bytes]; 22 | signal states[num_bytes+1][3]; 23 | signal states_tmp[num_bytes+1][3]; 24 | signal from_zero_enabled[num_bytes+1]; 25 | from_zero_enabled[num_bytes] <== 0; 26 | component state_changed[num_bytes]; 27 | 28 | for (var i = 1; i < 3; i++) { 29 | states[0][i] <== 0; 30 | } 31 | 32 | for (var i = 0; i < num_bytes; i++) { 33 | state_changed[i] = MultiOR(2); 34 | states[i][0] <== 1; 35 | eq[0][i] = IsEqual(); 36 | eq[0][i].in[0] <== in[i]; 37 | eq[0][i].in[1] <== 255; 38 | and[0][i] = AND(); 39 | and[0][i].a <== states[i][0]; 40 | and[0][i].b <== eq[0][i].out; 41 | states_tmp[i+1][1] <== 0; 42 | eq[1][i] = IsEqual(); 43 | eq[1][i].in[0] <== in[i]; 44 | eq[1][i].in[1] <== 97; 45 | and[1][i] = AND(); 46 | and[1][i].a <== states[i][1]; 47 | and[1][i].b <== eq[1][i].out; 48 | states[i+1][2] <== and[1][i].out; 49 | from_zero_enabled[i] <== MultiNOR(2)([states_tmp[i+1][1], states[i+1][2]]); 50 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 51 | state_changed[i].in[0] <== states[i+1][1]; 52 | state_changed[i].in[1] <== states[i+1][2]; 53 | } 54 | 55 | component is_accepted = MultiOR(num_bytes+1); 56 | for (var i = 0; i <= num_bytes; i++) { 57 | is_accepted.in[i] <== states[i][2]; 58 | } 59 | out <== is_accepted.out; 60 | signal is_consecutive[msg_bytes+1][3]; 61 | is_consecutive[msg_bytes][2] <== 0; 62 | for (var i = 0; i < msg_bytes; i++) { 63 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][2] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 64 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 65 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][2], is_consecutive[msg_bytes-1-i][1]]); 66 | } 67 | // substrings calculated: [{(1, 2)}] 68 | signal prev_states0[1][msg_bytes]; 69 | signal is_substr0[msg_bytes]; 70 | signal is_reveal0[msg_bytes]; 71 | signal output reveal0[msg_bytes]; 72 | for (var i = 0; i < msg_bytes; i++) { 73 | // the 0-th substring transitions: [(1, 2)] 74 | prev_states0[0][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][1]; 75 | is_substr0[i] <== MultiOR(1)([prev_states0[0][i] * states[i+2][2]]); 76 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 77 | reveal0[i] <== in[i+1] * is_reveal0[i]; 78 | } 79 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/caret2.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": true, 5 | "regex_def": "^(a|b|c)" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/caret2_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: ^(a|b|c) 6 | template Caret2Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[4][num_bytes]; 21 | component and[2][num_bytes]; 22 | component multi_or[1][num_bytes]; 23 | signal states[num_bytes+1][3]; 24 | signal states_tmp[num_bytes+1][3]; 25 | signal from_zero_enabled[num_bytes+1]; 26 | from_zero_enabled[num_bytes] <== 0; 27 | component state_changed[num_bytes]; 28 | 29 | for (var i = 1; i < 3; i++) { 30 | states[0][i] <== 0; 31 | } 32 | 33 | for (var i = 0; i < num_bytes; i++) { 34 | state_changed[i] = MultiOR(2); 35 | states[i][0] <== 1; 36 | eq[0][i] = IsEqual(); 37 | eq[0][i].in[0] <== in[i]; 38 | eq[0][i].in[1] <== 255; 39 | and[0][i] = AND(); 40 | and[0][i].a <== states[i][0]; 41 | and[0][i].b <== eq[0][i].out; 42 | states_tmp[i+1][1] <== 0; 43 | eq[1][i] = IsEqual(); 44 | eq[1][i].in[0] <== in[i]; 45 | eq[1][i].in[1] <== 97; 46 | eq[2][i] = IsEqual(); 47 | eq[2][i].in[0] <== in[i]; 48 | eq[2][i].in[1] <== 98; 49 | eq[3][i] = IsEqual(); 50 | eq[3][i].in[0] <== in[i]; 51 | eq[3][i].in[1] <== 99; 52 | and[1][i] = AND(); 53 | and[1][i].a <== states[i][1]; 54 | multi_or[0][i] = MultiOR(3); 55 | multi_or[0][i].in[0] <== eq[1][i].out; 56 | multi_or[0][i].in[1] <== eq[2][i].out; 57 | multi_or[0][i].in[2] <== eq[3][i].out; 58 | and[1][i].b <== multi_or[0][i].out; 59 | states[i+1][2] <== and[1][i].out; 60 | from_zero_enabled[i] <== MultiNOR(2)([states_tmp[i+1][1], states[i+1][2]]); 61 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 62 | state_changed[i].in[0] <== states[i+1][1]; 63 | state_changed[i].in[1] <== states[i+1][2]; 64 | } 65 | 66 | component is_accepted = MultiOR(num_bytes+1); 67 | for (var i = 0; i <= num_bytes; i++) { 68 | is_accepted.in[i] <== states[i][2]; 69 | } 70 | out <== is_accepted.out; 71 | signal is_consecutive[msg_bytes+1][3]; 72 | is_consecutive[msg_bytes][2] <== 0; 73 | for (var i = 0; i < msg_bytes; i++) { 74 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][2] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 75 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 76 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][2], is_consecutive[msg_bytes-1-i][1]]); 77 | } 78 | // substrings calculated: [{(1, 2)}] 79 | signal prev_states0[1][msg_bytes]; 80 | signal is_substr0[msg_bytes]; 81 | signal is_reveal0[msg_bytes]; 82 | signal output reveal0[msg_bytes]; 83 | for (var i = 0; i < msg_bytes; i++) { 84 | // the 0-th substring transitions: [(1, 2)] 85 | prev_states0[0][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][1]; 86 | is_substr0[i] <== MultiOR(1)([prev_states0[0][i] * states[i+2][2]]); 87 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 88 | reveal0[i] <== in[i+1] * is_reveal0[i]; 89 | } 90 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/caret3.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "(^|a)" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "b+" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/caret3_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: (^|a)b+ 6 | template Caret3Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[3][num_bytes]; 21 | component and[3][num_bytes]; 22 | component multi_or[2][num_bytes]; 23 | signal states[num_bytes+1][3]; 24 | signal states_tmp[num_bytes+1][3]; 25 | signal from_zero_enabled[num_bytes+1]; 26 | from_zero_enabled[num_bytes] <== 0; 27 | component state_changed[num_bytes]; 28 | 29 | for (var i = 1; i < 3; i++) { 30 | states[0][i] <== 0; 31 | } 32 | 33 | for (var i = 0; i < num_bytes; i++) { 34 | state_changed[i] = MultiOR(2); 35 | states[i][0] <== 1; 36 | eq[0][i] = IsEqual(); 37 | eq[0][i].in[0] <== in[i]; 38 | eq[0][i].in[1] <== 97; 39 | eq[1][i] = IsEqual(); 40 | eq[1][i].in[0] <== in[i]; 41 | eq[1][i].in[1] <== 255; 42 | and[0][i] = AND(); 43 | and[0][i].a <== states[i][0]; 44 | multi_or[0][i] = MultiOR(2); 45 | multi_or[0][i].in[0] <== eq[0][i].out; 46 | multi_or[0][i].in[1] <== eq[1][i].out; 47 | and[0][i].b <== multi_or[0][i].out; 48 | states_tmp[i+1][1] <== 0; 49 | eq[2][i] = IsEqual(); 50 | eq[2][i].in[0] <== in[i]; 51 | eq[2][i].in[1] <== 98; 52 | and[1][i] = AND(); 53 | and[1][i].a <== states[i][1]; 54 | and[1][i].b <== eq[2][i].out; 55 | and[2][i] = AND(); 56 | and[2][i].a <== states[i][2]; 57 | and[2][i].b <== eq[2][i].out; 58 | multi_or[1][i] = MultiOR(2); 59 | multi_or[1][i].in[0] <== and[1][i].out; 60 | multi_or[1][i].in[1] <== and[2][i].out; 61 | states[i+1][2] <== multi_or[1][i].out; 62 | from_zero_enabled[i] <== MultiNOR(2)([states_tmp[i+1][1], states[i+1][2]]); 63 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 64 | state_changed[i].in[0] <== states[i+1][1]; 65 | state_changed[i].in[1] <== states[i+1][2]; 66 | } 67 | 68 | component is_accepted = MultiOR(num_bytes+1); 69 | for (var i = 0; i <= num_bytes; i++) { 70 | is_accepted.in[i] <== states[i][2]; 71 | } 72 | out <== is_accepted.out; 73 | signal is_consecutive[msg_bytes+1][3]; 74 | is_consecutive[msg_bytes][2] <== 0; 75 | for (var i = 0; i < msg_bytes; i++) { 76 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][2] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 77 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 78 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][2], is_consecutive[msg_bytes-1-i][1]]); 79 | } 80 | // substrings calculated: [{(1, 2), (2, 2)}] 81 | signal prev_states0[2][msg_bytes]; 82 | signal is_substr0[msg_bytes]; 83 | signal is_reveal0[msg_bytes]; 84 | signal output reveal0[msg_bytes]; 85 | for (var i = 0; i < msg_bytes; i++) { 86 | // the 0-th substring transitions: [(1, 2), (2, 2)] 87 | prev_states0[0][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][1]; 88 | prev_states0[1][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][2]; 89 | is_substr0[i] <== MultiOR(2)([prev_states0[0][i] * states[i+2][2], prev_states0[1][i] * states[i+2][2]]); 90 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 91 | reveal0[i] <== in[i+1] * is_reveal0[i]; 92 | } 93 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/caret4.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "(\n|^)x" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "(a|b)+" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/caret4_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: (\n|^)x(a|b)+ 6 | template Caret4Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[5][num_bytes]; 21 | component and[4][num_bytes]; 22 | component multi_or[3][num_bytes]; 23 | signal states[num_bytes+1][4]; 24 | signal states_tmp[num_bytes+1][4]; 25 | signal from_zero_enabled[num_bytes+1]; 26 | from_zero_enabled[num_bytes] <== 0; 27 | component state_changed[num_bytes]; 28 | 29 | for (var i = 1; i < 4; i++) { 30 | states[0][i] <== 0; 31 | } 32 | 33 | for (var i = 0; i < num_bytes; i++) { 34 | state_changed[i] = MultiOR(3); 35 | states[i][0] <== 1; 36 | eq[0][i] = IsEqual(); 37 | eq[0][i].in[0] <== in[i]; 38 | eq[0][i].in[1] <== 10; 39 | eq[1][i] = IsEqual(); 40 | eq[1][i].in[0] <== in[i]; 41 | eq[1][i].in[1] <== 255; 42 | and[0][i] = AND(); 43 | and[0][i].a <== states[i][0]; 44 | multi_or[0][i] = MultiOR(2); 45 | multi_or[0][i].in[0] <== eq[0][i].out; 46 | multi_or[0][i].in[1] <== eq[1][i].out; 47 | and[0][i].b <== multi_or[0][i].out; 48 | states_tmp[i+1][1] <== 0; 49 | eq[2][i] = IsEqual(); 50 | eq[2][i].in[0] <== in[i]; 51 | eq[2][i].in[1] <== 120; 52 | and[1][i] = AND(); 53 | and[1][i].a <== states[i][1]; 54 | and[1][i].b <== eq[2][i].out; 55 | states[i+1][2] <== and[1][i].out; 56 | eq[3][i] = IsEqual(); 57 | eq[3][i].in[0] <== in[i]; 58 | eq[3][i].in[1] <== 97; 59 | eq[4][i] = IsEqual(); 60 | eq[4][i].in[0] <== in[i]; 61 | eq[4][i].in[1] <== 98; 62 | and[2][i] = AND(); 63 | and[2][i].a <== states[i][2]; 64 | multi_or[1][i] = MultiOR(2); 65 | multi_or[1][i].in[0] <== eq[3][i].out; 66 | multi_or[1][i].in[1] <== eq[4][i].out; 67 | and[2][i].b <== multi_or[1][i].out; 68 | and[3][i] = AND(); 69 | and[3][i].a <== states[i][3]; 70 | and[3][i].b <== multi_or[1][i].out; 71 | multi_or[2][i] = MultiOR(2); 72 | multi_or[2][i].in[0] <== and[2][i].out; 73 | multi_or[2][i].in[1] <== and[3][i].out; 74 | states[i+1][3] <== multi_or[2][i].out; 75 | from_zero_enabled[i] <== MultiNOR(3)([states_tmp[i+1][1], states[i+1][2], states[i+1][3]]); 76 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 77 | state_changed[i].in[0] <== states[i+1][1]; 78 | state_changed[i].in[1] <== states[i+1][2]; 79 | state_changed[i].in[2] <== states[i+1][3]; 80 | } 81 | 82 | component is_accepted = MultiOR(num_bytes+1); 83 | for (var i = 0; i <= num_bytes; i++) { 84 | is_accepted.in[i] <== states[i][3]; 85 | } 86 | out <== is_accepted.out; 87 | signal is_consecutive[msg_bytes+1][3]; 88 | is_consecutive[msg_bytes][2] <== 0; 89 | for (var i = 0; i < msg_bytes; i++) { 90 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][3] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 91 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 92 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][3], is_consecutive[msg_bytes-1-i][1]]); 93 | } 94 | // substrings calculated: [{(2, 3), (3, 3)}] 95 | signal prev_states0[2][msg_bytes]; 96 | signal is_substr0[msg_bytes]; 97 | signal is_reveal0[msg_bytes]; 98 | signal output reveal0[msg_bytes]; 99 | for (var i = 0; i < msg_bytes; i++) { 100 | // the 0-th substring transitions: [(2, 3), (3, 3)] 101 | prev_states0[0][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][2]; 102 | prev_states0[1][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][3]; 103 | is_substr0[i] <== MultiOR(2)([prev_states0[0][i] * states[i+2][3], prev_states0[1][i] * states[i+2][3]]); 104 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 105 | reveal0[i] <== in[i+1] * is_reveal0[i]; 106 | } 107 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/caret5.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "(\n|^)x" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "[^abc]+" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/dollar1.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": true, 5 | "regex_def": "a[bc]$" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/dollar1_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: a[bc]$ 6 | template Dollar1Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[3][num_bytes]; 21 | component and[2][num_bytes]; 22 | component multi_or[1][num_bytes]; 23 | signal states[num_bytes+1][3]; 24 | signal states_tmp[num_bytes+1][3]; 25 | signal from_zero_enabled[num_bytes+1]; 26 | from_zero_enabled[num_bytes] <== 0; 27 | component state_changed[num_bytes]; 28 | 29 | signal padding_start[num_bytes+1]; 30 | padding_start[0] <== 0; 31 | for (var i = 1; i < 3; i++) { 32 | states[0][i] <== 0; 33 | } 34 | 35 | for (var i = 0; i < num_bytes; i++) { 36 | state_changed[i] = MultiOR(2); 37 | states[i][0] <== 1; 38 | padding_start[i+1] <== IsNotZeroAcc()(padding_start[i], in[i]); 39 | eq[0][i] = IsEqual(); 40 | eq[0][i].in[0] <== in[i]; 41 | eq[0][i].in[1] <== 97; 42 | and[0][i] = AND(); 43 | and[0][i].a <== states[i][0]; 44 | and[0][i].b <== eq[0][i].out; 45 | states_tmp[i+1][1] <== 0; 46 | eq[1][i] = IsEqual(); 47 | eq[1][i].in[0] <== in[i]; 48 | eq[1][i].in[1] <== 98; 49 | eq[2][i] = IsEqual(); 50 | eq[2][i].in[0] <== in[i]; 51 | eq[2][i].in[1] <== 99; 52 | and[1][i] = AND(); 53 | and[1][i].a <== states[i][1]; 54 | multi_or[0][i] = MultiOR(2); 55 | multi_or[0][i].in[0] <== eq[1][i].out; 56 | multi_or[0][i].in[1] <== eq[2][i].out; 57 | and[1][i].b <== multi_or[0][i].out; 58 | states[i+1][2] <== and[1][i].out; 59 | from_zero_enabled[i] <== MultiNOR(2)([states_tmp[i+1][1], states[i+1][2]]); 60 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 61 | state_changed[i].in[0] <== states[i+1][1]; 62 | state_changed[i].in[1] <== states[i+1][2]; 63 | } 64 | 65 | component is_accepted = MultiOR(num_bytes+1); 66 | for (var i = 0; i <= num_bytes; i++) { 67 | is_accepted.in[i] <== states[i][2]; 68 | } 69 | signal end_anchor_check[num_bytes+1][2]; 70 | end_anchor_check[0][1] <== 0; 71 | for (var i = 0; i < num_bytes; i++) { 72 | end_anchor_check[i+1][0] <== IsEqual()([i, padding_start[num_bytes]]); 73 | end_anchor_check[i+1][1] <== end_anchor_check[i][1] + states[i][2] * end_anchor_check[i+1][0]; 74 | } 75 | out <== is_accepted.out * end_anchor_check[num_bytes][1]; 76 | signal is_consecutive[msg_bytes+1][3]; 77 | is_consecutive[msg_bytes][2] <== 0; 78 | for (var i = 0; i < msg_bytes; i++) { 79 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][2] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 80 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 81 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][2], is_consecutive[msg_bytes-1-i][1]]); 82 | } 83 | // substrings calculated: [{(0, 1), (1, 2)}] 84 | signal prev_states0[2][msg_bytes]; 85 | signal is_substr0[msg_bytes]; 86 | signal is_reveal0[msg_bytes]; 87 | signal output reveal0[msg_bytes]; 88 | for (var i = 0; i < msg_bytes; i++) { 89 | // the 0-th substring transitions: [(0, 1), (1, 2)] 90 | prev_states0[0][i] <== from_zero_enabled[i+1] * states[i+1][0]; 91 | prev_states0[1][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][1]; 92 | is_substr0[i] <== MultiOR(2)([prev_states0[0][i] * states[i+2][1], prev_states0[1][i] * states[i+2][2]]); 93 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 94 | reveal0[i] <== in[i+1] * is_reveal0[i]; 95 | } 96 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/dollar2.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "(\n|^)x" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "a[bc]$" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/dollar2_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: (\n|^)xa[bc]$ 6 | template Dollar2Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[6][num_bytes]; 21 | component and[4][num_bytes]; 22 | component multi_or[2][num_bytes]; 23 | signal states[num_bytes+1][5]; 24 | signal states_tmp[num_bytes+1][5]; 25 | signal from_zero_enabled[num_bytes+1]; 26 | from_zero_enabled[num_bytes] <== 0; 27 | component state_changed[num_bytes]; 28 | 29 | signal padding_start[num_bytes+1]; 30 | padding_start[0] <== 0; 31 | for (var i = 1; i < 5; i++) { 32 | states[0][i] <== 0; 33 | } 34 | 35 | for (var i = 0; i < num_bytes; i++) { 36 | state_changed[i] = MultiOR(4); 37 | states[i][0] <== 1; 38 | padding_start[i+1] <== IsNotZeroAcc()(padding_start[i], in[i]); 39 | eq[0][i] = IsEqual(); 40 | eq[0][i].in[0] <== in[i]; 41 | eq[0][i].in[1] <== 10; 42 | eq[1][i] = IsEqual(); 43 | eq[1][i].in[0] <== in[i]; 44 | eq[1][i].in[1] <== 255; 45 | and[0][i] = AND(); 46 | and[0][i].a <== states[i][0]; 47 | multi_or[0][i] = MultiOR(2); 48 | multi_or[0][i].in[0] <== eq[0][i].out; 49 | multi_or[0][i].in[1] <== eq[1][i].out; 50 | and[0][i].b <== multi_or[0][i].out; 51 | states_tmp[i+1][1] <== 0; 52 | eq[2][i] = IsEqual(); 53 | eq[2][i].in[0] <== in[i]; 54 | eq[2][i].in[1] <== 120; 55 | and[1][i] = AND(); 56 | and[1][i].a <== states[i][1]; 57 | and[1][i].b <== eq[2][i].out; 58 | states[i+1][2] <== and[1][i].out; 59 | eq[3][i] = IsEqual(); 60 | eq[3][i].in[0] <== in[i]; 61 | eq[3][i].in[1] <== 97; 62 | and[2][i] = AND(); 63 | and[2][i].a <== states[i][2]; 64 | and[2][i].b <== eq[3][i].out; 65 | states[i+1][3] <== and[2][i].out; 66 | eq[4][i] = IsEqual(); 67 | eq[4][i].in[0] <== in[i]; 68 | eq[4][i].in[1] <== 98; 69 | eq[5][i] = IsEqual(); 70 | eq[5][i].in[0] <== in[i]; 71 | eq[5][i].in[1] <== 99; 72 | and[3][i] = AND(); 73 | and[3][i].a <== states[i][3]; 74 | multi_or[1][i] = MultiOR(2); 75 | multi_or[1][i].in[0] <== eq[4][i].out; 76 | multi_or[1][i].in[1] <== eq[5][i].out; 77 | and[3][i].b <== multi_or[1][i].out; 78 | states[i+1][4] <== and[3][i].out; 79 | from_zero_enabled[i] <== MultiNOR(4)([states_tmp[i+1][1], states[i+1][2], states[i+1][3], states[i+1][4]]); 80 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 81 | state_changed[i].in[0] <== states[i+1][1]; 82 | state_changed[i].in[1] <== states[i+1][2]; 83 | state_changed[i].in[2] <== states[i+1][3]; 84 | state_changed[i].in[3] <== states[i+1][4]; 85 | } 86 | 87 | component is_accepted = MultiOR(num_bytes+1); 88 | for (var i = 0; i <= num_bytes; i++) { 89 | is_accepted.in[i] <== states[i][4]; 90 | } 91 | signal end_anchor_check[num_bytes+1][2]; 92 | end_anchor_check[0][1] <== 0; 93 | for (var i = 0; i < num_bytes; i++) { 94 | end_anchor_check[i+1][0] <== IsEqual()([i, padding_start[num_bytes]]); 95 | end_anchor_check[i+1][1] <== end_anchor_check[i][1] + states[i][4] * end_anchor_check[i+1][0]; 96 | } 97 | out <== is_accepted.out * end_anchor_check[num_bytes][1]; 98 | signal is_consecutive[msg_bytes+1][3]; 99 | is_consecutive[msg_bytes][2] <== 0; 100 | for (var i = 0; i < msg_bytes; i++) { 101 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][4] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 102 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 103 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][4], is_consecutive[msg_bytes-1-i][1]]); 104 | } 105 | // substrings calculated: [{(2, 3), (3, 4)}] 106 | signal prev_states0[2][msg_bytes]; 107 | signal is_substr0[msg_bytes]; 108 | signal is_reveal0[msg_bytes]; 109 | signal output reveal0[msg_bytes]; 110 | for (var i = 0; i < msg_bytes; i++) { 111 | // the 0-th substring transitions: [(2, 3), (3, 4)] 112 | prev_states0[0][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][2]; 113 | prev_states0[1][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][3]; 114 | is_substr0[i] <== MultiOR(2)([prev_states0[0][i] * states[i+2][3], prev_states0[1][i] * states[i+2][4]]); 115 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 116 | reveal0[i] <== in[i+1] * is_reveal0[i]; 117 | } 118 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/dot1.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": true, 5 | "regex_def": "." 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/dot2.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "a" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "." 10 | }, 11 | { 12 | "is_public": false, 13 | "regex_def": "b" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/international_chars_decomposed.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "Latin-Extension=" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "[¡-ƿ]+" 10 | }, 11 | { 12 | "is_public": false, 13 | "regex_def": " Greek=" 14 | }, 15 | { 16 | "is_public": true, 17 | "regex_def": "[Ͱ-Ͽ]+" 18 | }, 19 | { 20 | "is_public": false, 21 | "regex_def": " Cyrillic=" 22 | }, 23 | { 24 | "is_public": true, 25 | "regex_def": "[Ѐ-ӿ]+" 26 | }, 27 | { 28 | "is_public": false, 29 | "regex_def": " Arabic=" 30 | }, 31 | { 32 | "is_public": true, 33 | "regex_def": "[؀-ۿ]+" 34 | }, 35 | { 36 | "is_public": false, 37 | "regex_def": " Devanagari=" 38 | }, 39 | { 40 | "is_public": true, 41 | "regex_def": "[ऀ-ॿ]+" 42 | }, 43 | { 44 | "is_public": false, 45 | "regex_def": " Hiragana&Katakana=" 46 | }, 47 | { 48 | "is_public": true, 49 | "regex_def": "[ぁ-ヿ]+" 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /packages/circom/tests/circuits/invitation_code_with_prefix.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": true, 5 | "regex_def": "( )?(c|C)ode( )?(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)+" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/negate1.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "a:" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "[^abcdefghijklmnopqrstuvwxyz\\.]+" 10 | }, 11 | { 12 | "is_public": false, 13 | "regex_def": "\\." 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/negate2.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": true, 5 | "regex_def": "[^ab]" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/plus1.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": true, 5 | "regex_def": "a+" 6 | }, 7 | { 8 | "is_public": false, 9 | "regex_def": "b" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/plus1_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: a+b 6 | template Plus1Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[2][num_bytes]; 21 | component and[3][num_bytes]; 22 | signal states[num_bytes+1][3]; 23 | signal states_tmp[num_bytes+1][3]; 24 | signal from_zero_enabled[num_bytes+1]; 25 | from_zero_enabled[num_bytes] <== 0; 26 | component state_changed[num_bytes]; 27 | 28 | for (var i = 1; i < 3; i++) { 29 | states[0][i] <== 0; 30 | } 31 | 32 | for (var i = 0; i < num_bytes; i++) { 33 | state_changed[i] = MultiOR(2); 34 | states[i][0] <== 1; 35 | eq[0][i] = IsEqual(); 36 | eq[0][i].in[0] <== in[i]; 37 | eq[0][i].in[1] <== 97; 38 | and[0][i] = AND(); 39 | and[0][i].a <== states[i][0]; 40 | and[0][i].b <== eq[0][i].out; 41 | and[1][i] = AND(); 42 | and[1][i].a <== states[i][1]; 43 | and[1][i].b <== eq[0][i].out; 44 | states_tmp[i+1][1] <== and[1][i].out; 45 | eq[1][i] = IsEqual(); 46 | eq[1][i].in[0] <== in[i]; 47 | eq[1][i].in[1] <== 98; 48 | and[2][i] = AND(); 49 | and[2][i].a <== states[i][1]; 50 | and[2][i].b <== eq[1][i].out; 51 | states[i+1][2] <== and[2][i].out; 52 | from_zero_enabled[i] <== MultiNOR(2)([states_tmp[i+1][1], states[i+1][2]]); 53 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 54 | state_changed[i].in[0] <== states[i+1][1]; 55 | state_changed[i].in[1] <== states[i+1][2]; 56 | } 57 | 58 | component is_accepted = MultiOR(num_bytes+1); 59 | for (var i = 0; i <= num_bytes; i++) { 60 | is_accepted.in[i] <== states[i][2]; 61 | } 62 | out <== is_accepted.out; 63 | signal is_consecutive[msg_bytes+1][3]; 64 | is_consecutive[msg_bytes][2] <== 0; 65 | for (var i = 0; i < msg_bytes; i++) { 66 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][2] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 67 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 68 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][2], is_consecutive[msg_bytes-1-i][1]]); 69 | } 70 | // substrings calculated: [{(0, 1), (1, 1)}] 71 | signal prev_states0[2][msg_bytes]; 72 | signal is_substr0[msg_bytes]; 73 | signal is_reveal0[msg_bytes]; 74 | signal output reveal0[msg_bytes]; 75 | for (var i = 0; i < msg_bytes; i++) { 76 | // the 0-th substring transitions: [(0, 1), (1, 1)] 77 | prev_states0[0][i] <== from_zero_enabled[i+1] * states[i+1][0]; 78 | prev_states0[1][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][1]; 79 | is_substr0[i] <== MultiOR(2)([prev_states0[0][i] * states[i+2][1], prev_states0[1][i] * states[i+2][1]]); 80 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 81 | reveal0[i] <== in[i+1] * is_reveal0[i]; 82 | } 83 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/plus2.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "a" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "(b|c)+" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/plus2_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: a(b|c)+ 6 | template Plus2Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[3][num_bytes]; 21 | component and[3][num_bytes]; 22 | component multi_or[2][num_bytes]; 23 | signal states[num_bytes+1][3]; 24 | signal states_tmp[num_bytes+1][3]; 25 | signal from_zero_enabled[num_bytes+1]; 26 | from_zero_enabled[num_bytes] <== 0; 27 | component state_changed[num_bytes]; 28 | 29 | for (var i = 1; i < 3; i++) { 30 | states[0][i] <== 0; 31 | } 32 | 33 | for (var i = 0; i < num_bytes; i++) { 34 | state_changed[i] = MultiOR(2); 35 | states[i][0] <== 1; 36 | eq[0][i] = IsEqual(); 37 | eq[0][i].in[0] <== in[i]; 38 | eq[0][i].in[1] <== 97; 39 | and[0][i] = AND(); 40 | and[0][i].a <== states[i][0]; 41 | and[0][i].b <== eq[0][i].out; 42 | states_tmp[i+1][1] <== 0; 43 | eq[1][i] = IsEqual(); 44 | eq[1][i].in[0] <== in[i]; 45 | eq[1][i].in[1] <== 98; 46 | eq[2][i] = IsEqual(); 47 | eq[2][i].in[0] <== in[i]; 48 | eq[2][i].in[1] <== 99; 49 | and[1][i] = AND(); 50 | and[1][i].a <== states[i][1]; 51 | multi_or[0][i] = MultiOR(2); 52 | multi_or[0][i].in[0] <== eq[1][i].out; 53 | multi_or[0][i].in[1] <== eq[2][i].out; 54 | and[1][i].b <== multi_or[0][i].out; 55 | and[2][i] = AND(); 56 | and[2][i].a <== states[i][2]; 57 | and[2][i].b <== multi_or[0][i].out; 58 | multi_or[1][i] = MultiOR(2); 59 | multi_or[1][i].in[0] <== and[1][i].out; 60 | multi_or[1][i].in[1] <== and[2][i].out; 61 | states[i+1][2] <== multi_or[1][i].out; 62 | from_zero_enabled[i] <== MultiNOR(2)([states_tmp[i+1][1], states[i+1][2]]); 63 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 64 | state_changed[i].in[0] <== states[i+1][1]; 65 | state_changed[i].in[1] <== states[i+1][2]; 66 | } 67 | 68 | component is_accepted = MultiOR(num_bytes+1); 69 | for (var i = 0; i <= num_bytes; i++) { 70 | is_accepted.in[i] <== states[i][2]; 71 | } 72 | out <== is_accepted.out; 73 | signal is_consecutive[msg_bytes+1][3]; 74 | is_consecutive[msg_bytes][2] <== 0; 75 | for (var i = 0; i < msg_bytes; i++) { 76 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][2] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 77 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 78 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][2], is_consecutive[msg_bytes-1-i][1]]); 79 | } 80 | // substrings calculated: [{(1, 2), (2, 2)}] 81 | signal prev_states0[2][msg_bytes]; 82 | signal is_substr0[msg_bytes]; 83 | signal is_reveal0[msg_bytes]; 84 | signal output reveal0[msg_bytes]; 85 | for (var i = 0; i < msg_bytes; i++) { 86 | // the 0-th substring transitions: [(1, 2), (2, 2)] 87 | prev_states0[0][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][1]; 88 | prev_states0[1][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][2]; 89 | is_substr0[i] <== MultiOR(2)([prev_states0[0][i] * states[i+2][2], prev_states0[1][i] * states[i+2][2]]); 90 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 91 | reveal0[i] <== in[i+1] * is_reveal0[i]; 92 | } 93 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/plus3.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "a" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "(bc)+" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/plus3_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: a(bc)+ 6 | template Plus3Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[3][num_bytes]; 21 | component and[4][num_bytes]; 22 | component multi_or[1][num_bytes]; 23 | signal states[num_bytes+1][4]; 24 | signal states_tmp[num_bytes+1][4]; 25 | signal from_zero_enabled[num_bytes+1]; 26 | from_zero_enabled[num_bytes] <== 0; 27 | component state_changed[num_bytes]; 28 | 29 | for (var i = 1; i < 4; i++) { 30 | states[0][i] <== 0; 31 | } 32 | 33 | for (var i = 0; i < num_bytes; i++) { 34 | state_changed[i] = MultiOR(3); 35 | states[i][0] <== 1; 36 | eq[0][i] = IsEqual(); 37 | eq[0][i].in[0] <== in[i]; 38 | eq[0][i].in[1] <== 97; 39 | and[0][i] = AND(); 40 | and[0][i].a <== states[i][0]; 41 | and[0][i].b <== eq[0][i].out; 42 | states_tmp[i+1][1] <== 0; 43 | eq[1][i] = IsEqual(); 44 | eq[1][i].in[0] <== in[i]; 45 | eq[1][i].in[1] <== 98; 46 | and[1][i] = AND(); 47 | and[1][i].a <== states[i][1]; 48 | and[1][i].b <== eq[1][i].out; 49 | and[2][i] = AND(); 50 | and[2][i].a <== states[i][3]; 51 | and[2][i].b <== eq[1][i].out; 52 | multi_or[0][i] = MultiOR(2); 53 | multi_or[0][i].in[0] <== and[1][i].out; 54 | multi_or[0][i].in[1] <== and[2][i].out; 55 | states[i+1][2] <== multi_or[0][i].out; 56 | eq[2][i] = IsEqual(); 57 | eq[2][i].in[0] <== in[i]; 58 | eq[2][i].in[1] <== 99; 59 | and[3][i] = AND(); 60 | and[3][i].a <== states[i][2]; 61 | and[3][i].b <== eq[2][i].out; 62 | states[i+1][3] <== and[3][i].out; 63 | from_zero_enabled[i] <== MultiNOR(3)([states_tmp[i+1][1], states[i+1][2], states[i+1][3]]); 64 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 65 | state_changed[i].in[0] <== states[i+1][1]; 66 | state_changed[i].in[1] <== states[i+1][2]; 67 | state_changed[i].in[2] <== states[i+1][3]; 68 | } 69 | 70 | component is_accepted = MultiOR(num_bytes+1); 71 | for (var i = 0; i <= num_bytes; i++) { 72 | is_accepted.in[i] <== states[i][3]; 73 | } 74 | out <== is_accepted.out; 75 | signal is_consecutive[msg_bytes+1][3]; 76 | is_consecutive[msg_bytes][2] <== 0; 77 | for (var i = 0; i < msg_bytes; i++) { 78 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][3] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 79 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 80 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][3], is_consecutive[msg_bytes-1-i][1]]); 81 | } 82 | // substrings calculated: [{(1, 2), (2, 3), (3, 2)}] 83 | signal prev_states0[3][msg_bytes]; 84 | signal is_substr0[msg_bytes]; 85 | signal is_reveal0[msg_bytes]; 86 | signal output reveal0[msg_bytes]; 87 | for (var i = 0; i < msg_bytes; i++) { 88 | // the 0-th substring transitions: [(1, 2), (2, 3), (3, 2)] 89 | prev_states0[0][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][1]; 90 | prev_states0[1][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][2]; 91 | prev_states0[2][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][3]; 92 | is_substr0[i] <== MultiOR(3)([prev_states0[0][i] * states[i+2][2], prev_states0[1][i] * states[i+2][3], prev_states0[2][i] * states[i+2][2]]); 93 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 94 | reveal0[i] <== in[i+1] * is_reveal0[i]; 95 | } 96 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/plus4.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": true, 5 | "regex_def": "(12|345)+" 6 | }, 7 | { 8 | "is_public": false, 9 | "regex_def": "b" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/plus4_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: (12|345)+b 6 | template Plus4Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[6][num_bytes]; 21 | component and[8][num_bytes]; 22 | component multi_or[1][num_bytes]; 23 | signal states[num_bytes+1][6]; 24 | signal states_tmp[num_bytes+1][6]; 25 | signal from_zero_enabled[num_bytes+1]; 26 | from_zero_enabled[num_bytes] <== 0; 27 | component state_changed[num_bytes]; 28 | 29 | for (var i = 1; i < 6; i++) { 30 | states[0][i] <== 0; 31 | } 32 | 33 | for (var i = 0; i < num_bytes; i++) { 34 | state_changed[i] = MultiOR(5); 35 | states[i][0] <== 1; 36 | eq[0][i] = IsEqual(); 37 | eq[0][i].in[0] <== in[i]; 38 | eq[0][i].in[1] <== 49; 39 | and[0][i] = AND(); 40 | and[0][i].a <== states[i][0]; 41 | and[0][i].b <== eq[0][i].out; 42 | and[1][i] = AND(); 43 | and[1][i].a <== states[i][3]; 44 | and[1][i].b <== eq[0][i].out; 45 | states_tmp[i+1][1] <== and[1][i].out; 46 | eq[1][i] = IsEqual(); 47 | eq[1][i].in[0] <== in[i]; 48 | eq[1][i].in[1] <== 51; 49 | and[2][i] = AND(); 50 | and[2][i].a <== states[i][0]; 51 | and[2][i].b <== eq[1][i].out; 52 | and[3][i] = AND(); 53 | and[3][i].a <== states[i][3]; 54 | and[3][i].b <== eq[1][i].out; 55 | states_tmp[i+1][2] <== and[3][i].out; 56 | eq[2][i] = IsEqual(); 57 | eq[2][i].in[0] <== in[i]; 58 | eq[2][i].in[1] <== 50; 59 | and[4][i] = AND(); 60 | and[4][i].a <== states[i][1]; 61 | and[4][i].b <== eq[2][i].out; 62 | eq[3][i] = IsEqual(); 63 | eq[3][i].in[0] <== in[i]; 64 | eq[3][i].in[1] <== 53; 65 | and[5][i] = AND(); 66 | and[5][i].a <== states[i][4]; 67 | and[5][i].b <== eq[3][i].out; 68 | multi_or[0][i] = MultiOR(2); 69 | multi_or[0][i].in[0] <== and[4][i].out; 70 | multi_or[0][i].in[1] <== and[5][i].out; 71 | states[i+1][3] <== multi_or[0][i].out; 72 | eq[4][i] = IsEqual(); 73 | eq[4][i].in[0] <== in[i]; 74 | eq[4][i].in[1] <== 52; 75 | and[6][i] = AND(); 76 | and[6][i].a <== states[i][2]; 77 | and[6][i].b <== eq[4][i].out; 78 | states[i+1][4] <== and[6][i].out; 79 | eq[5][i] = IsEqual(); 80 | eq[5][i].in[0] <== in[i]; 81 | eq[5][i].in[1] <== 98; 82 | and[7][i] = AND(); 83 | and[7][i].a <== states[i][3]; 84 | and[7][i].b <== eq[5][i].out; 85 | states[i+1][5] <== and[7][i].out; 86 | from_zero_enabled[i] <== MultiNOR(5)([states_tmp[i+1][1], states_tmp[i+1][2], states[i+1][3], states[i+1][4], states[i+1][5]]); 87 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 88 | states[i+1][2] <== MultiOR(2)([states_tmp[i+1][2], from_zero_enabled[i] * and[2][i].out]); 89 | state_changed[i].in[0] <== states[i+1][1]; 90 | state_changed[i].in[1] <== states[i+1][2]; 91 | state_changed[i].in[2] <== states[i+1][3]; 92 | state_changed[i].in[3] <== states[i+1][4]; 93 | state_changed[i].in[4] <== states[i+1][5]; 94 | } 95 | 96 | component is_accepted = MultiOR(num_bytes+1); 97 | for (var i = 0; i <= num_bytes; i++) { 98 | is_accepted.in[i] <== states[i][5]; 99 | } 100 | out <== is_accepted.out; 101 | signal is_consecutive[msg_bytes+1][3]; 102 | is_consecutive[msg_bytes][2] <== 0; 103 | for (var i = 0; i < msg_bytes; i++) { 104 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][5] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 105 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 106 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][5], is_consecutive[msg_bytes-1-i][1]]); 107 | } 108 | // substrings calculated: [{(0, 1), (0, 2), (1, 3), (2, 4), (3, 1), (3, 2), (4, 3)}] 109 | signal prev_states0[7][msg_bytes]; 110 | signal is_substr0[msg_bytes]; 111 | signal is_reveal0[msg_bytes]; 112 | signal output reveal0[msg_bytes]; 113 | for (var i = 0; i < msg_bytes; i++) { 114 | // the 0-th substring transitions: [(0, 1), (0, 2), (1, 3), (2, 4), (3, 1), (3, 2), (4, 3)] 115 | prev_states0[0][i] <== from_zero_enabled[i+1] * states[i+1][0]; 116 | prev_states0[1][i] <== from_zero_enabled[i+1] * states[i+1][0]; 117 | prev_states0[2][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][1]; 118 | prev_states0[3][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][2]; 119 | prev_states0[4][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][3]; 120 | prev_states0[5][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][3]; 121 | prev_states0[6][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][4]; 122 | is_substr0[i] <== MultiOR(7)([prev_states0[0][i] * states[i+2][1], prev_states0[1][i] * states[i+2][2], prev_states0[2][i] * states[i+2][3], prev_states0[3][i] * states[i+2][4], prev_states0[4][i] * states[i+2][1], prev_states0[5][i] * states[i+2][2], prev_states0[6][i] * states[i+2][3]]); 123 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 124 | reveal0[i] <== in[i+1] * is_reveal0[i]; 125 | } 126 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/question1.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": true, 5 | "regex_def": "a?" 6 | }, 7 | { 8 | "is_public": false, 9 | "regex_def": "b" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/question1_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: a?b 6 | template Question1Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[2][num_bytes]; 21 | component and[3][num_bytes]; 22 | signal states[num_bytes+1][3]; 23 | signal states_tmp[num_bytes+1][3]; 24 | signal from_zero_enabled[num_bytes+1]; 25 | from_zero_enabled[num_bytes] <== 0; 26 | component state_changed[num_bytes]; 27 | 28 | for (var i = 1; i < 3; i++) { 29 | states[0][i] <== 0; 30 | } 31 | 32 | for (var i = 0; i < num_bytes; i++) { 33 | state_changed[i] = MultiOR(2); 34 | states[i][0] <== 1; 35 | eq[0][i] = IsEqual(); 36 | eq[0][i].in[0] <== in[i]; 37 | eq[0][i].in[1] <== 97; 38 | and[0][i] = AND(); 39 | and[0][i].a <== states[i][0]; 40 | and[0][i].b <== eq[0][i].out; 41 | states_tmp[i+1][1] <== 0; 42 | eq[1][i] = IsEqual(); 43 | eq[1][i].in[0] <== in[i]; 44 | eq[1][i].in[1] <== 98; 45 | and[1][i] = AND(); 46 | and[1][i].a <== states[i][0]; 47 | and[1][i].b <== eq[1][i].out; 48 | and[2][i] = AND(); 49 | and[2][i].a <== states[i][1]; 50 | and[2][i].b <== eq[1][i].out; 51 | states_tmp[i+1][2] <== and[2][i].out; 52 | from_zero_enabled[i] <== MultiNOR(2)([states_tmp[i+1][1], states_tmp[i+1][2]]); 53 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 54 | states[i+1][2] <== MultiOR(2)([states_tmp[i+1][2], from_zero_enabled[i] * and[1][i].out]); 55 | state_changed[i].in[0] <== states[i+1][1]; 56 | state_changed[i].in[1] <== states[i+1][2]; 57 | } 58 | 59 | component is_accepted = MultiOR(num_bytes+1); 60 | for (var i = 0; i <= num_bytes; i++) { 61 | is_accepted.in[i] <== states[i][2]; 62 | } 63 | out <== is_accepted.out; 64 | signal is_consecutive[msg_bytes+1][3]; 65 | is_consecutive[msg_bytes][2] <== 0; 66 | for (var i = 0; i < msg_bytes; i++) { 67 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][2] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 68 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 69 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][2], is_consecutive[msg_bytes-1-i][1]]); 70 | } 71 | // substrings calculated: [{(0, 1)}] 72 | signal prev_states0[1][msg_bytes]; 73 | signal is_substr0[msg_bytes]; 74 | signal is_reveal0[msg_bytes]; 75 | signal output reveal0[msg_bytes]; 76 | for (var i = 0; i < msg_bytes; i++) { 77 | // the 0-th substring transitions: [(0, 1)] 78 | prev_states0[0][i] <== from_zero_enabled[i+1] * states[i+1][0]; 79 | is_substr0[i] <== MultiOR(1)([prev_states0[0][i] * states[i+2][1]]); 80 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 81 | reveal0[i] <== in[i+1] * is_reveal0[i]; 82 | } 83 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/question2.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": true, 5 | "regex_def": "(1x?2)+" 6 | }, 7 | { 8 | "is_public": false, 9 | "regex_def": "b" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/question2_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: (1x?2)+b 6 | template Question2Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[4][num_bytes]; 21 | component and[6][num_bytes]; 22 | component multi_or[1][num_bytes]; 23 | signal states[num_bytes+1][5]; 24 | signal states_tmp[num_bytes+1][5]; 25 | signal from_zero_enabled[num_bytes+1]; 26 | from_zero_enabled[num_bytes] <== 0; 27 | component state_changed[num_bytes]; 28 | 29 | for (var i = 1; i < 5; i++) { 30 | states[0][i] <== 0; 31 | } 32 | 33 | for (var i = 0; i < num_bytes; i++) { 34 | state_changed[i] = MultiOR(4); 35 | states[i][0] <== 1; 36 | eq[0][i] = IsEqual(); 37 | eq[0][i].in[0] <== in[i]; 38 | eq[0][i].in[1] <== 49; 39 | and[0][i] = AND(); 40 | and[0][i].a <== states[i][0]; 41 | and[0][i].b <== eq[0][i].out; 42 | and[1][i] = AND(); 43 | and[1][i].a <== states[i][2]; 44 | and[1][i].b <== eq[0][i].out; 45 | states_tmp[i+1][1] <== and[1][i].out; 46 | eq[1][i] = IsEqual(); 47 | eq[1][i].in[0] <== in[i]; 48 | eq[1][i].in[1] <== 50; 49 | and[2][i] = AND(); 50 | and[2][i].a <== states[i][1]; 51 | and[2][i].b <== eq[1][i].out; 52 | and[3][i] = AND(); 53 | and[3][i].a <== states[i][3]; 54 | and[3][i].b <== eq[1][i].out; 55 | multi_or[0][i] = MultiOR(2); 56 | multi_or[0][i].in[0] <== and[2][i].out; 57 | multi_or[0][i].in[1] <== and[3][i].out; 58 | states[i+1][2] <== multi_or[0][i].out; 59 | eq[2][i] = IsEqual(); 60 | eq[2][i].in[0] <== in[i]; 61 | eq[2][i].in[1] <== 120; 62 | and[4][i] = AND(); 63 | and[4][i].a <== states[i][1]; 64 | and[4][i].b <== eq[2][i].out; 65 | states[i+1][3] <== and[4][i].out; 66 | eq[3][i] = IsEqual(); 67 | eq[3][i].in[0] <== in[i]; 68 | eq[3][i].in[1] <== 98; 69 | and[5][i] = AND(); 70 | and[5][i].a <== states[i][2]; 71 | and[5][i].b <== eq[3][i].out; 72 | states[i+1][4] <== and[5][i].out; 73 | from_zero_enabled[i] <== MultiNOR(4)([states_tmp[i+1][1], states[i+1][2], states[i+1][3], states[i+1][4]]); 74 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 75 | state_changed[i].in[0] <== states[i+1][1]; 76 | state_changed[i].in[1] <== states[i+1][2]; 77 | state_changed[i].in[2] <== states[i+1][3]; 78 | state_changed[i].in[3] <== states[i+1][4]; 79 | } 80 | 81 | component is_accepted = MultiOR(num_bytes+1); 82 | for (var i = 0; i <= num_bytes; i++) { 83 | is_accepted.in[i] <== states[i][4]; 84 | } 85 | out <== is_accepted.out; 86 | signal is_consecutive[msg_bytes+1][3]; 87 | is_consecutive[msg_bytes][2] <== 0; 88 | for (var i = 0; i < msg_bytes; i++) { 89 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][4] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 90 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 91 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][4], is_consecutive[msg_bytes-1-i][1]]); 92 | } 93 | // substrings calculated: [{(0, 1), (1, 2), (1, 3), (2, 1), (3, 2)}] 94 | signal prev_states0[5][msg_bytes]; 95 | signal is_substr0[msg_bytes]; 96 | signal is_reveal0[msg_bytes]; 97 | signal output reveal0[msg_bytes]; 98 | for (var i = 0; i < msg_bytes; i++) { 99 | // the 0-th substring transitions: [(0, 1), (1, 2), (1, 3), (2, 1), (3, 2)] 100 | prev_states0[0][i] <== from_zero_enabled[i+1] * states[i+1][0]; 101 | prev_states0[1][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][1]; 102 | prev_states0[2][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][1]; 103 | prev_states0[3][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][2]; 104 | prev_states0[4][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][3]; 105 | is_substr0[i] <== MultiOR(5)([prev_states0[0][i] * states[i+2][1], prev_states0[1][i] * states[i+2][2], prev_states0[2][i] * states[i+2][3], prev_states0[3][i] * states[i+2][1], prev_states0[4][i] * states[i+2][2]]); 106 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 107 | reveal0[i] <== in[i+1] * is_reveal0[i]; 108 | } 109 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/question3.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "12" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "(a|b)?" 10 | }, 11 | { 12 | "is_public": false, 13 | "regex_def": "c" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/question3_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: 12(a|b)?c 6 | template Question3Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[5][num_bytes]; 21 | component and[5][num_bytes]; 22 | component multi_or[2][num_bytes]; 23 | signal states[num_bytes+1][5]; 24 | signal states_tmp[num_bytes+1][5]; 25 | signal from_zero_enabled[num_bytes+1]; 26 | from_zero_enabled[num_bytes] <== 0; 27 | component state_changed[num_bytes]; 28 | 29 | for (var i = 1; i < 5; i++) { 30 | states[0][i] <== 0; 31 | } 32 | 33 | for (var i = 0; i < num_bytes; i++) { 34 | state_changed[i] = MultiOR(4); 35 | states[i][0] <== 1; 36 | eq[0][i] = IsEqual(); 37 | eq[0][i].in[0] <== in[i]; 38 | eq[0][i].in[1] <== 49; 39 | and[0][i] = AND(); 40 | and[0][i].a <== states[i][0]; 41 | and[0][i].b <== eq[0][i].out; 42 | states_tmp[i+1][1] <== 0; 43 | eq[1][i] = IsEqual(); 44 | eq[1][i].in[0] <== in[i]; 45 | eq[1][i].in[1] <== 50; 46 | and[1][i] = AND(); 47 | and[1][i].a <== states[i][1]; 48 | and[1][i].b <== eq[1][i].out; 49 | states[i+1][2] <== and[1][i].out; 50 | eq[2][i] = IsEqual(); 51 | eq[2][i].in[0] <== in[i]; 52 | eq[2][i].in[1] <== 97; 53 | eq[3][i] = IsEqual(); 54 | eq[3][i].in[0] <== in[i]; 55 | eq[3][i].in[1] <== 98; 56 | and[2][i] = AND(); 57 | and[2][i].a <== states[i][2]; 58 | multi_or[0][i] = MultiOR(2); 59 | multi_or[0][i].in[0] <== eq[2][i].out; 60 | multi_or[0][i].in[1] <== eq[3][i].out; 61 | and[2][i].b <== multi_or[0][i].out; 62 | states[i+1][3] <== and[2][i].out; 63 | eq[4][i] = IsEqual(); 64 | eq[4][i].in[0] <== in[i]; 65 | eq[4][i].in[1] <== 99; 66 | and[3][i] = AND(); 67 | and[3][i].a <== states[i][2]; 68 | and[3][i].b <== eq[4][i].out; 69 | and[4][i] = AND(); 70 | and[4][i].a <== states[i][3]; 71 | and[4][i].b <== eq[4][i].out; 72 | multi_or[1][i] = MultiOR(2); 73 | multi_or[1][i].in[0] <== and[3][i].out; 74 | multi_or[1][i].in[1] <== and[4][i].out; 75 | states[i+1][4] <== multi_or[1][i].out; 76 | from_zero_enabled[i] <== MultiNOR(4)([states_tmp[i+1][1], states[i+1][2], states[i+1][3], states[i+1][4]]); 77 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 78 | state_changed[i].in[0] <== states[i+1][1]; 79 | state_changed[i].in[1] <== states[i+1][2]; 80 | state_changed[i].in[2] <== states[i+1][3]; 81 | state_changed[i].in[3] <== states[i+1][4]; 82 | } 83 | 84 | component is_accepted = MultiOR(num_bytes+1); 85 | for (var i = 0; i <= num_bytes; i++) { 86 | is_accepted.in[i] <== states[i][4]; 87 | } 88 | out <== is_accepted.out; 89 | signal is_consecutive[msg_bytes+1][3]; 90 | is_consecutive[msg_bytes][2] <== 0; 91 | for (var i = 0; i < msg_bytes; i++) { 92 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][4] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 93 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 94 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][4], is_consecutive[msg_bytes-1-i][1]]); 95 | } 96 | // substrings calculated: [{(2, 3)}] 97 | signal prev_states0[1][msg_bytes]; 98 | signal is_substr0[msg_bytes]; 99 | signal is_reveal0[msg_bytes]; 100 | signal output reveal0[msg_bytes]; 101 | for (var i = 0; i < msg_bytes; i++) { 102 | // the 0-th substring transitions: [(2, 3)] 103 | prev_states0[0][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][2]; 104 | is_substr0[i] <== MultiOR(1)([prev_states0[0][i] * states[i+2][3]]); 105 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 106 | reveal0[i] <== in[i+1] * is_reveal0[i]; 107 | } 108 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/reveal_check1.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": true, 5 | "regex_def": "aba" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/reveal_check1_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: aba 6 | template RevealCheck1Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[2][num_bytes]; 21 | component and[3][num_bytes]; 22 | signal states[num_bytes+1][4]; 23 | signal states_tmp[num_bytes+1][4]; 24 | signal from_zero_enabled[num_bytes+1]; 25 | from_zero_enabled[num_bytes] <== 0; 26 | component state_changed[num_bytes]; 27 | 28 | for (var i = 1; i < 4; i++) { 29 | states[0][i] <== 0; 30 | } 31 | 32 | for (var i = 0; i < num_bytes; i++) { 33 | state_changed[i] = MultiOR(3); 34 | states[i][0] <== 1; 35 | eq[0][i] = IsEqual(); 36 | eq[0][i].in[0] <== in[i]; 37 | eq[0][i].in[1] <== 97; 38 | and[0][i] = AND(); 39 | and[0][i].a <== states[i][0]; 40 | and[0][i].b <== eq[0][i].out; 41 | states_tmp[i+1][1] <== 0; 42 | eq[1][i] = IsEqual(); 43 | eq[1][i].in[0] <== in[i]; 44 | eq[1][i].in[1] <== 98; 45 | and[1][i] = AND(); 46 | and[1][i].a <== states[i][1]; 47 | and[1][i].b <== eq[1][i].out; 48 | states[i+1][2] <== and[1][i].out; 49 | and[2][i] = AND(); 50 | and[2][i].a <== states[i][2]; 51 | and[2][i].b <== eq[0][i].out; 52 | states[i+1][3] <== and[2][i].out; 53 | from_zero_enabled[i] <== MultiNOR(3)([states_tmp[i+1][1], states[i+1][2], states[i+1][3]]); 54 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 55 | state_changed[i].in[0] <== states[i+1][1]; 56 | state_changed[i].in[1] <== states[i+1][2]; 57 | state_changed[i].in[2] <== states[i+1][3]; 58 | } 59 | 60 | component is_accepted = MultiOR(num_bytes+1); 61 | for (var i = 0; i <= num_bytes; i++) { 62 | is_accepted.in[i] <== states[i][3]; 63 | } 64 | out <== is_accepted.out; 65 | signal is_consecutive[msg_bytes+1][3]; 66 | is_consecutive[msg_bytes][2] <== 0; 67 | for (var i = 0; i < msg_bytes; i++) { 68 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][3] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 69 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 70 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][3], is_consecutive[msg_bytes-1-i][1]]); 71 | } 72 | // substrings calculated: [{(0, 1), (1, 2), (2, 3)}] 73 | signal prev_states0[3][msg_bytes]; 74 | signal is_substr0[msg_bytes]; 75 | signal is_reveal0[msg_bytes]; 76 | signal output reveal0[msg_bytes]; 77 | for (var i = 0; i < msg_bytes; i++) { 78 | // the 0-th substring transitions: [(0, 1), (1, 2), (2, 3)] 79 | prev_states0[0][i] <== from_zero_enabled[i+1] * states[i+1][0]; 80 | prev_states0[1][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][1]; 81 | prev_states0[2][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][2]; 82 | is_substr0[i] <== MultiOR(3)([prev_states0[0][i] * states[i+2][1], prev_states0[1][i] * states[i+2][2], prev_states0[2][i] * states[i+2][3]]); 83 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 84 | reveal0[i] <== in[i+1] * is_reveal0[i]; 85 | } 86 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/reveal_check2.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": true, 5 | "regex_def": "a[ab]" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/reveal_check2_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: a[ab] 6 | template RevealCheck2Regex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[2][num_bytes]; 21 | component and[2][num_bytes]; 22 | component multi_or[1][num_bytes]; 23 | signal states[num_bytes+1][3]; 24 | signal states_tmp[num_bytes+1][3]; 25 | signal from_zero_enabled[num_bytes+1]; 26 | from_zero_enabled[num_bytes] <== 0; 27 | component state_changed[num_bytes]; 28 | 29 | for (var i = 1; i < 3; i++) { 30 | states[0][i] <== 0; 31 | } 32 | 33 | for (var i = 0; i < num_bytes; i++) { 34 | state_changed[i] = MultiOR(2); 35 | states[i][0] <== 1; 36 | eq[0][i] = IsEqual(); 37 | eq[0][i].in[0] <== in[i]; 38 | eq[0][i].in[1] <== 97; 39 | and[0][i] = AND(); 40 | and[0][i].a <== states[i][0]; 41 | and[0][i].b <== eq[0][i].out; 42 | states_tmp[i+1][1] <== 0; 43 | eq[1][i] = IsEqual(); 44 | eq[1][i].in[0] <== in[i]; 45 | eq[1][i].in[1] <== 98; 46 | and[1][i] = AND(); 47 | and[1][i].a <== states[i][1]; 48 | multi_or[0][i] = MultiOR(2); 49 | multi_or[0][i].in[0] <== eq[0][i].out; 50 | multi_or[0][i].in[1] <== eq[1][i].out; 51 | and[1][i].b <== multi_or[0][i].out; 52 | states[i+1][2] <== and[1][i].out; 53 | from_zero_enabled[i] <== MultiNOR(2)([states_tmp[i+1][1], states[i+1][2]]); 54 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 55 | state_changed[i].in[0] <== states[i+1][1]; 56 | state_changed[i].in[1] <== states[i+1][2]; 57 | } 58 | 59 | component is_accepted = MultiOR(num_bytes+1); 60 | for (var i = 0; i <= num_bytes; i++) { 61 | is_accepted.in[i] <== states[i][2]; 62 | } 63 | out <== is_accepted.out; 64 | signal is_consecutive[msg_bytes+1][3]; 65 | is_consecutive[msg_bytes][2] <== 0; 66 | for (var i = 0; i < msg_bytes; i++) { 67 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][2] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 68 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 69 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][2], is_consecutive[msg_bytes-1-i][1]]); 70 | } 71 | // substrings calculated: [{(0, 1), (1, 2)}] 72 | signal prev_states0[2][msg_bytes]; 73 | signal is_substr0[msg_bytes]; 74 | signal is_reveal0[msg_bytes]; 75 | signal output reveal0[msg_bytes]; 76 | for (var i = 0; i < msg_bytes; i++) { 77 | // the 0-th substring transitions: [(0, 1), (1, 2)] 78 | prev_states0[0][i] <== from_zero_enabled[i+1] * states[i+1][0]; 79 | prev_states0[1][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][1]; 80 | is_substr0[i] <== MultiOR(2)([prev_states0[0][i] * states[i+2][1], prev_states0[1][i] * states[i+2][2]]); 81 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 82 | reveal0[i] <== in[i+1] * is_reveal0[i]; 83 | } 84 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/simple_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "@zk-email/zk-regex-circom/circuits/regex_helpers.circom"; 4 | 5 | // regex: 1=(a|b) (2=(b|c)+ )+d 6 | template SimpleRegex(msg_bytes) { 7 | signal input msg[msg_bytes]; 8 | signal output out; 9 | 10 | var num_bytes = msg_bytes+1; 11 | signal in[num_bytes]; 12 | signal in_range_checks[msg_bytes]; 13 | in[0]<==255; 14 | for (var i = 0; i < msg_bytes; i++) { 15 | in_range_checks[i] <== LessThan(8)([msg[i], 255]); 16 | in_range_checks[i] === 1; 17 | in[i+1] <== msg[i]; 18 | } 19 | 20 | component eq[8][num_bytes]; 21 | component and[11][num_bytes]; 22 | component multi_or[4][num_bytes]; 23 | signal states[num_bytes+1][10]; 24 | signal states_tmp[num_bytes+1][10]; 25 | signal from_zero_enabled[num_bytes+1]; 26 | from_zero_enabled[num_bytes] <== 0; 27 | component state_changed[num_bytes]; 28 | 29 | for (var i = 1; i < 10; i++) { 30 | states[0][i] <== 0; 31 | } 32 | 33 | for (var i = 0; i < num_bytes; i++) { 34 | state_changed[i] = MultiOR(9); 35 | states[i][0] <== 1; 36 | eq[0][i] = IsEqual(); 37 | eq[0][i].in[0] <== in[i]; 38 | eq[0][i].in[1] <== 49; 39 | and[0][i] = AND(); 40 | and[0][i].a <== states[i][0]; 41 | and[0][i].b <== eq[0][i].out; 42 | states_tmp[i+1][1] <== 0; 43 | eq[1][i] = IsEqual(); 44 | eq[1][i].in[0] <== in[i]; 45 | eq[1][i].in[1] <== 61; 46 | and[1][i] = AND(); 47 | and[1][i].a <== states[i][1]; 48 | and[1][i].b <== eq[1][i].out; 49 | states[i+1][2] <== and[1][i].out; 50 | eq[2][i] = IsEqual(); 51 | eq[2][i].in[0] <== in[i]; 52 | eq[2][i].in[1] <== 97; 53 | eq[3][i] = IsEqual(); 54 | eq[3][i].in[0] <== in[i]; 55 | eq[3][i].in[1] <== 98; 56 | and[2][i] = AND(); 57 | and[2][i].a <== states[i][2]; 58 | multi_or[0][i] = MultiOR(2); 59 | multi_or[0][i].in[0] <== eq[2][i].out; 60 | multi_or[0][i].in[1] <== eq[3][i].out; 61 | and[2][i].b <== multi_or[0][i].out; 62 | states[i+1][3] <== and[2][i].out; 63 | eq[4][i] = IsEqual(); 64 | eq[4][i].in[0] <== in[i]; 65 | eq[4][i].in[1] <== 32; 66 | and[3][i] = AND(); 67 | and[3][i].a <== states[i][3]; 68 | and[3][i].b <== eq[4][i].out; 69 | states[i+1][4] <== and[3][i].out; 70 | eq[5][i] = IsEqual(); 71 | eq[5][i].in[0] <== in[i]; 72 | eq[5][i].in[1] <== 50; 73 | and[4][i] = AND(); 74 | and[4][i].a <== states[i][4]; 75 | and[4][i].b <== eq[5][i].out; 76 | and[5][i] = AND(); 77 | and[5][i].a <== states[i][8]; 78 | and[5][i].b <== eq[5][i].out; 79 | multi_or[1][i] = MultiOR(2); 80 | multi_or[1][i].in[0] <== and[4][i].out; 81 | multi_or[1][i].in[1] <== and[5][i].out; 82 | states[i+1][5] <== multi_or[1][i].out; 83 | and[6][i] = AND(); 84 | and[6][i].a <== states[i][5]; 85 | and[6][i].b <== eq[1][i].out; 86 | states[i+1][6] <== and[6][i].out; 87 | eq[6][i] = IsEqual(); 88 | eq[6][i].in[0] <== in[i]; 89 | eq[6][i].in[1] <== 99; 90 | and[7][i] = AND(); 91 | and[7][i].a <== states[i][6]; 92 | multi_or[2][i] = MultiOR(2); 93 | multi_or[2][i].in[0] <== eq[3][i].out; 94 | multi_or[2][i].in[1] <== eq[6][i].out; 95 | and[7][i].b <== multi_or[2][i].out; 96 | and[8][i] = AND(); 97 | and[8][i].a <== states[i][7]; 98 | and[8][i].b <== multi_or[2][i].out; 99 | multi_or[3][i] = MultiOR(2); 100 | multi_or[3][i].in[0] <== and[7][i].out; 101 | multi_or[3][i].in[1] <== and[8][i].out; 102 | states[i+1][7] <== multi_or[3][i].out; 103 | and[9][i] = AND(); 104 | and[9][i].a <== states[i][7]; 105 | and[9][i].b <== eq[4][i].out; 106 | states[i+1][8] <== and[9][i].out; 107 | eq[7][i] = IsEqual(); 108 | eq[7][i].in[0] <== in[i]; 109 | eq[7][i].in[1] <== 100; 110 | and[10][i] = AND(); 111 | and[10][i].a <== states[i][8]; 112 | and[10][i].b <== eq[7][i].out; 113 | states[i+1][9] <== and[10][i].out; 114 | from_zero_enabled[i] <== MultiNOR(9)([states_tmp[i+1][1], states[i+1][2], states[i+1][3], states[i+1][4], states[i+1][5], states[i+1][6], states[i+1][7], states[i+1][8], states[i+1][9]]); 115 | states[i+1][1] <== MultiOR(2)([states_tmp[i+1][1], from_zero_enabled[i] * and[0][i].out]); 116 | state_changed[i].in[0] <== states[i+1][1]; 117 | state_changed[i].in[1] <== states[i+1][2]; 118 | state_changed[i].in[2] <== states[i+1][3]; 119 | state_changed[i].in[3] <== states[i+1][4]; 120 | state_changed[i].in[4] <== states[i+1][5]; 121 | state_changed[i].in[5] <== states[i+1][6]; 122 | state_changed[i].in[6] <== states[i+1][7]; 123 | state_changed[i].in[7] <== states[i+1][8]; 124 | state_changed[i].in[8] <== states[i+1][9]; 125 | } 126 | 127 | component is_accepted = MultiOR(num_bytes+1); 128 | for (var i = 0; i <= num_bytes; i++) { 129 | is_accepted.in[i] <== states[i][9]; 130 | } 131 | out <== is_accepted.out; 132 | signal is_consecutive[msg_bytes+1][3]; 133 | is_consecutive[msg_bytes][2] <== 0; 134 | for (var i = 0; i < msg_bytes; i++) { 135 | is_consecutive[msg_bytes-1-i][0] <== states[num_bytes-i][9] * (1 - is_consecutive[msg_bytes-i][2]) + is_consecutive[msg_bytes-i][2]; 136 | is_consecutive[msg_bytes-1-i][1] <== state_changed[msg_bytes-i].out * is_consecutive[msg_bytes-1-i][0]; 137 | is_consecutive[msg_bytes-1-i][2] <== ORAnd()([(1 - from_zero_enabled[msg_bytes-i+1]), states[num_bytes-i][9], is_consecutive[msg_bytes-1-i][1]]); 138 | } 139 | // substrings calculated: [{(2, 3)}, {(6, 7), (7, 7)}, {(8, 9)}] 140 | signal prev_states0[1][msg_bytes]; 141 | signal is_substr0[msg_bytes]; 142 | signal is_reveal0[msg_bytes]; 143 | signal output reveal0[msg_bytes]; 144 | for (var i = 0; i < msg_bytes; i++) { 145 | // the 0-th substring transitions: [(2, 3)] 146 | prev_states0[0][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][2]; 147 | is_substr0[i] <== MultiOR(1)([prev_states0[0][i] * states[i+2][3]]); 148 | is_reveal0[i] <== MultiAND(3)([out, is_substr0[i], is_consecutive[i][2]]); 149 | reveal0[i] <== in[i+1] * is_reveal0[i]; 150 | } 151 | signal prev_states1[2][msg_bytes]; 152 | signal is_substr1[msg_bytes]; 153 | signal is_reveal1[msg_bytes]; 154 | signal output reveal1[msg_bytes]; 155 | for (var i = 0; i < msg_bytes; i++) { 156 | // the 1-th substring transitions: [(6, 7), (7, 7)] 157 | prev_states1[0][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][6]; 158 | prev_states1[1][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][7]; 159 | is_substr1[i] <== MultiOR(2)([prev_states1[0][i] * states[i+2][7], prev_states1[1][i] * states[i+2][7]]); 160 | is_reveal1[i] <== MultiAND(3)([out, is_substr1[i], is_consecutive[i][2]]); 161 | reveal1[i] <== in[i+1] * is_reveal1[i]; 162 | } 163 | signal prev_states2[1][msg_bytes]; 164 | signal is_substr2[msg_bytes]; 165 | signal is_reveal2[msg_bytes]; 166 | signal output reveal2[msg_bytes]; 167 | for (var i = 0; i < msg_bytes; i++) { 168 | // the 2-th substring transitions: [(8, 9)] 169 | prev_states2[0][i] <== (1 - from_zero_enabled[i+1]) * states[i+1][8]; 170 | is_substr2[i] <== MultiOR(1)([prev_states2[0][i] * states[i+2][9]]); 171 | is_reveal2[i] <== MultiAND(3)([out, is_substr2[i], is_consecutive[i][2]]); 172 | reveal2[i] <== in[i+1] * is_reveal2[i]; 173 | } 174 | } -------------------------------------------------------------------------------- /packages/circom/tests/circuits/simple_regex_decomposed.json: -------------------------------------------------------------------------------- 1 | { 2 | "parts": [ 3 | { 4 | "is_public": false, 5 | "regex_def": "email was meant for @" 6 | }, 7 | { 8 | "is_public": true, 9 | "regex_def": "[a-zA-Z0-9_]+" 10 | }, 11 | { 12 | "is_public": false, 13 | "regex_def": "\\." 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/circom/tests/circuits/simple_regex_substrs.json: -------------------------------------------------------------------------------- 1 | { 2 | "transitions": [ 3 | [[2, 3]], 4 | [ 5 | [6, 7], 6 | [7, 7] 7 | ], 8 | [[8, 9]] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_asterisk1_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./asterisk1_regex.circom"; 4 | 5 | component main = Asterisk1Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_asterisk2_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./asterisk2_regex.circom"; 4 | 5 | component main = Asterisk2Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_asterisk3_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./asterisk3_regex.circom"; 4 | 5 | component main = Asterisk3Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_body_hash_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "../../circuits/common/body_hash_regex.circom"; 4 | 5 | component main = BodyHashRegex(1024); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_caret1_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./caret1_regex.circom"; 4 | 5 | component main = Caret1Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_caret2_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./caret2_regex.circom"; 4 | 5 | component main = Caret2Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_caret3_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./caret3_regex.circom"; 4 | 5 | component main = Caret3Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_caret4_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./caret4_regex.circom"; 4 | 5 | component main = Caret4Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_caret5_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./caret5_regex.circom"; 4 | 5 | component main = Caret5Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_dollar1_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./dollar1_regex.circom"; 4 | 5 | component main = Dollar1Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_dollar2_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./dollar2_regex.circom"; 4 | 5 | component main = Dollar2Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_dot1_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./dot1_regex.circom"; 4 | 5 | component main = Dot1Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_dot2_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./dot2_regex.circom"; 4 | 5 | component main = Dot2Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_email_addr_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "../../circuits/common/email_addr_regex.circom"; 4 | 5 | component main = EmailAddrRegex(256); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_email_domain_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "../../circuits/common/email_domain_regex.circom"; 4 | 5 | component main = EmailDomainRegex(256); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_from_addr_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "../../circuits/common/from_addr_regex.circom"; 4 | 5 | component main = FromAddrRegex(1024); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_international_chars_decomposed.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./international_chars_decomposed.circom"; 4 | // Latin-Extension=[¡-ƿ]+ Greek=[Ͱ-Ͽ]+ Cyrillic=[Ѐ-ӿ]+ Arabic=[؀-ۿ]+ Devanagari=[ऀ-ॿ]+ Hiragana&Katakana=[ぁ-ヿ]+ 5 | component main = InternationalCharsDecomposed(128); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_invitation_code_with_prefix_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./invitation_code_with_prefix_regex.circom"; 4 | 5 | component main = InvitationCodeWithPrefixRegex(256); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_message_id_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "../../circuits/common/message_id_regex.circom"; 4 | 5 | component main = MessageIdRegex(256); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_negate1_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./negate1_regex.circom"; 4 | component main = Negate1Regex(64); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_negate2_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./negate2_regex.circom"; 4 | component main = Negate2Regex(64); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_plus1_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./plus1_regex.circom"; 4 | 5 | component main = Plus1Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_plus2_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./plus2_regex.circom"; 4 | 5 | component main = Plus2Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_plus3_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./plus3_regex.circom"; 4 | 5 | component main = Plus3Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_plus4_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./plus4_regex.circom"; 4 | 5 | component main = Plus4Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_question1_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./question1_regex.circom"; 4 | 5 | component main = Question1Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_question2_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./question2_regex.circom"; 4 | 5 | component main = Question2Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_question3_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./question3_regex.circom"; 4 | 5 | component main = Question3Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_reveal_check1_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./reveal_check1_regex.circom"; 4 | 5 | component main = RevealCheck1Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_reveal_check2_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./reveal_check2_regex.circom"; 4 | 5 | component main = RevealCheck2Regex(8); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_simple_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | include "./simple_regex.circom"; 3 | // 1=(a|b) (2=(b|c)+ )+d 4 | component main = SimpleRegex(64); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_simple_regex_decomposed.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "./simple_regex_decomposed.circom"; 4 | // email was meant for @[a-zA-Z0-9_]+\. 5 | component main = SimpleRegexDecomposed(64); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_subject_all_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "../../circuits/common/subject_all_regex.circom"; 4 | 5 | component main = SubjectAllRegex(256); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_timestamp_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "../../circuits/common/timestamp_regex.circom"; 4 | 5 | component main = TimestampRegex(1024); -------------------------------------------------------------------------------- /packages/circom/tests/circuits/test_to_addr_regex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "../../circuits/common/to_addr_regex.circom"; 4 | 5 | component main = ToAddrRegex(1024); -------------------------------------------------------------------------------- /packages/circom/tests/dollar.test.js: -------------------------------------------------------------------------------- 1 | import circom_tester from 'circom_tester'; 2 | import * as path from 'path'; 3 | import { readFileSync, writeFileSync } from 'fs'; 4 | import apis from '../../apis/pkg'; 5 | import compiler from '../../compiler/pkg'; 6 | const option = { 7 | include: path.join(__dirname, '../../../node_modules') 8 | }; 9 | const wasm_tester = circom_tester.wasm; 10 | 11 | jest.setTimeout(600000); 12 | describe('Caret Regex', () => { 13 | let circuit1; 14 | let circuit2; 15 | beforeAll(async () => { 16 | writeFileSync( 17 | path.join(__dirname, './circuits/dollar1_regex.circom'), 18 | compiler.genFromDecomposed( 19 | readFileSync( 20 | path.join(__dirname, './circuits/dollar1.json'), 21 | 'utf8' 22 | ), 23 | 'Dollar1Regex' 24 | ) 25 | ); 26 | circuit1 = await wasm_tester( 27 | path.join(__dirname, './circuits/test_dollar1_regex.circom'), 28 | option 29 | ); 30 | 31 | writeFileSync( 32 | path.join(__dirname, './circuits/dollar2_regex.circom'), 33 | compiler.genFromDecomposed( 34 | readFileSync( 35 | path.join(__dirname, './circuits/dollar2.json'), 36 | 'utf8' 37 | ), 38 | 'Dollar2Regex' 39 | ) 40 | ); 41 | circuit2 = await wasm_tester( 42 | path.join(__dirname, './circuits/test_dollar2_regex.circom'), 43 | option 44 | ); 45 | }); 46 | 47 | it('dollar1 valid case 1', async () => { 48 | const inputStr = `ab`; 49 | const paddedStr = apis.padString(inputStr, 8); 50 | const circuitInputs = { 51 | msg: paddedStr 52 | }; 53 | const witness = await circuit1.calculateWitness(circuitInputs); 54 | await circuit1.checkConstraints(witness); 55 | expect(1n).toEqual(witness[1]); 56 | const prefixIdxes = apis.extractSubstrIdxes( 57 | inputStr, 58 | readFileSync( 59 | path.join(__dirname, './circuits/dollar1.json'), 60 | 'utf8' 61 | ), 62 | false 63 | )[0]; 64 | for (let idx = 0; idx < 8; ++idx) { 65 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 66 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 67 | } else { 68 | expect(0n).toEqual(witness[2 + idx]); 69 | } 70 | } 71 | }); 72 | 73 | it('dollar1 invalid case 1', async () => { 74 | const inputStr = `abg`; 75 | const paddedStr = apis.padString(inputStr, 8); 76 | const circuitInputs = { 77 | msg: paddedStr 78 | }; 79 | const witness = await circuit1.calculateWitness(circuitInputs); 80 | await circuit1.checkConstraints(witness); 81 | expect(0n).toEqual(witness[1]); 82 | for (let idx = 0; idx < 8; ++idx) { 83 | expect(0n).toEqual(witness[2 + idx]); 84 | } 85 | }); 86 | 87 | it('dollar1 invalid case 2', async () => { 88 | const inputStr = `18abcg`; 89 | const paddedStr = apis.padString(inputStr, 8); 90 | const circuitInputs = { 91 | msg: paddedStr 92 | }; 93 | const witness = await circuit1.calculateWitness(circuitInputs); 94 | await circuit1.checkConstraints(witness); 95 | expect(0n).toEqual(witness[1]); 96 | for (let idx = 0; idx < 8; ++idx) { 97 | expect(0n).toEqual(witness[2 + idx]); 98 | } 99 | }); 100 | 101 | it('dollar2 valid case 1', async () => { 102 | const inputStr = `xab`; 103 | const paddedStr = apis.padString(inputStr, 8); 104 | const circuitInputs = { 105 | msg: paddedStr 106 | }; 107 | const witness = await circuit2.calculateWitness(circuitInputs); 108 | await circuit2.checkConstraints(witness); 109 | expect(1n).toEqual(witness[1]); 110 | const prefixIdxes = apis.extractSubstrIdxes( 111 | inputStr, 112 | readFileSync( 113 | path.join(__dirname, './circuits/dollar2.json'), 114 | 'utf8' 115 | ), 116 | false 117 | )[0]; 118 | for (let idx = 0; idx < 8; ++idx) { 119 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 120 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 121 | } else { 122 | expect(0n).toEqual(witness[2 + idx]); 123 | } 124 | } 125 | }); 126 | 127 | it('dollar2 valid case 2', async () => { 128 | const inputStr = `ak\nxab`; 129 | const paddedStr = apis.padString(inputStr, 8); 130 | const circuitInputs = { 131 | msg: paddedStr 132 | }; 133 | const witness = await circuit2.calculateWitness(circuitInputs); 134 | await circuit2.checkConstraints(witness); 135 | expect(1n).toEqual(witness[1]); 136 | const prefixIdxes = apis.extractSubstrIdxes( 137 | inputStr, 138 | readFileSync( 139 | path.join(__dirname, './circuits/dollar2.json'), 140 | 'utf8' 141 | ), 142 | false 143 | )[0]; 144 | for (let idx = 0; idx < 8; ++idx) { 145 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 146 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 147 | } else { 148 | expect(0n).toEqual(witness[2 + idx]); 149 | } 150 | } 151 | }); 152 | 153 | it('dollar2 invalid case 1', async () => { 154 | const inputStr = `abg`; 155 | const paddedStr = apis.padString(inputStr, 8); 156 | const circuitInputs = { 157 | msg: paddedStr 158 | }; 159 | const witness = await circuit2.calculateWitness(circuitInputs); 160 | await circuit2.checkConstraints(witness); 161 | expect(0n).toEqual(witness[1]); 162 | for (let idx = 0; idx < 8; ++idx) { 163 | expect(0n).toEqual(witness[2 + idx]); 164 | } 165 | }); 166 | 167 | it('dollar2 invalid case 2', async () => { 168 | const inputStr = `\nabg`; 169 | const paddedStr = apis.padString(inputStr, 8); 170 | const circuitInputs = { 171 | msg: paddedStr 172 | }; 173 | const witness = await circuit2.calculateWitness(circuitInputs); 174 | await circuit2.checkConstraints(witness); 175 | expect(0n).toEqual(witness[1]); 176 | for (let idx = 0; idx < 8; ++idx) { 177 | expect(0n).toEqual(witness[2 + idx]); 178 | } 179 | }); 180 | 181 | it('dollar2 invalid case 2', async () => { 182 | const inputStr = `\nabg`; 183 | const paddedStr = apis.padString(inputStr, 8); 184 | const circuitInputs = { 185 | msg: paddedStr 186 | }; 187 | const witness = await circuit2.calculateWitness(circuitInputs); 188 | await circuit2.checkConstraints(witness); 189 | expect(0n).toEqual(witness[1]); 190 | for (let idx = 0; idx < 8; ++idx) { 191 | expect(0n).toEqual(witness[2 + idx]); 192 | } 193 | }); 194 | }); 195 | -------------------------------------------------------------------------------- /packages/circom/tests/dot.test.js: -------------------------------------------------------------------------------- 1 | import circom_tester from 'circom_tester'; 2 | import * as path from 'path'; 3 | import { readFileSync, writeFileSync } from 'fs'; 4 | import apis from '../../apis/pkg'; 5 | import compiler from '../../compiler/pkg'; 6 | const option = { 7 | include: path.join(__dirname, '../../../node_modules') 8 | }; 9 | const wasm_tester = circom_tester.wasm; 10 | 11 | jest.setTimeout(600000); 12 | describe('Dot Regex', () => { 13 | let circuit1; 14 | let circuit2; 15 | beforeAll(async () => { 16 | writeFileSync( 17 | path.join(__dirname, './circuits/dot1_regex.circom'), 18 | compiler.genFromDecomposed( 19 | readFileSync( 20 | path.join(__dirname, './circuits/dot1.json'), 21 | 'utf8' 22 | ), 23 | 'Dot1Regex' 24 | ) 25 | ); 26 | circuit1 = await wasm_tester( 27 | path.join(__dirname, './circuits/test_dot1_regex.circom'), 28 | option 29 | ); 30 | 31 | writeFileSync( 32 | path.join(__dirname, './circuits/dot2_regex.circom'), 33 | compiler.genFromDecomposed( 34 | readFileSync( 35 | path.join(__dirname, './circuits/dot2.json'), 36 | 'utf8' 37 | ), 38 | 'Dot2Regex' 39 | ) 40 | ); 41 | circuit2 = await wasm_tester( 42 | path.join(__dirname, './circuits/test_dot2_regex.circom'), 43 | option 44 | ); 45 | }); 46 | 47 | it('dot1 valid case 1', async () => { 48 | const inputStr = `a`; 49 | const paddedStr = apis.padString(inputStr, 8); 50 | const circuitInputs = { 51 | msg: paddedStr 52 | }; 53 | const witness = await circuit1.calculateWitness(circuitInputs); 54 | await circuit1.checkConstraints(witness); 55 | expect(1n).toEqual(witness[1]); 56 | const prefixIdxes = apis.extractSubstrIdxes( 57 | inputStr, 58 | readFileSync(path.join(__dirname, './circuits/dot1.json'), 'utf8'), 59 | false 60 | )[0]; 61 | for (let idx = 0; idx < 8; ++idx) { 62 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 63 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 64 | } else { 65 | expect(0n).toEqual(witness[2 + idx]); 66 | } 67 | } 68 | }); 69 | 70 | // it("dot1 valid case 2", async () => { 71 | // const inputStr = `aaaa`; 72 | // const paddedStr = apis.padString(inputStr, 8); 73 | // const circuitInputs = { 74 | // msg: paddedStr, 75 | // }; 76 | // const witness = await circuit1.calculateWitness(circuitInputs); 77 | // await circuit1.checkConstraints(witness); 78 | // expect(1n).toEqual(witness[1]); 79 | // const prefixIdxes = apis.extractSubstrIdxes( 80 | // inputStr, 81 | // readFileSync( 82 | // path.join(__dirname, "./circuits/dot1.json"), 83 | // "utf8" 84 | // ),false 85 | // )[0]; 86 | // for (let idx = 0; idx < 8; ++idx) { 87 | // if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 88 | // expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 89 | // } else { 90 | // expect(0n).toEqual(witness[2 + idx]); 91 | // } 92 | // } 93 | // }); 94 | 95 | it('dot2 valid case 1', async () => { 96 | const inputStr = `a6b`; 97 | const paddedStr = apis.padString(inputStr, 8); 98 | const circuitInputs = { 99 | msg: paddedStr 100 | }; 101 | const witness = await circuit2.calculateWitness(circuitInputs); 102 | await circuit2.checkConstraints(witness); 103 | expect(1n).toEqual(witness[1]); 104 | const prefixIdxes = apis.extractSubstrIdxes( 105 | inputStr, 106 | readFileSync(path.join(__dirname, './circuits/dot2.json'), 'utf8'), 107 | false 108 | )[0]; 109 | for (let idx = 0; idx < 8; ++idx) { 110 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 111 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 112 | } else { 113 | expect(0n).toEqual(witness[2 + idx]); 114 | } 115 | } 116 | }); 117 | 118 | // it("dot2 valid case 2", async () => { 119 | // const inputStr = `aa6b`; 120 | // const paddedStr = apis.padString(inputStr, 8); 121 | // const circuitInputs = { 122 | // msg: paddedStr, 123 | // }; 124 | // const witness = await circuit2.calculateWitness(circuitInputs); 125 | // await circuit2.checkConstraints(witness); 126 | // expect(1n).toEqual(witness[1]); 127 | // const prefixIdxes = apis.extractSubstrIdxes( 128 | // inputStr, 129 | // readFileSync( 130 | // path.join(__dirname, "./circuits/dot2.json"), 131 | // "utf8" 132 | // ),false 133 | // )[0]; 134 | // for (let idx = 0; idx < 8; ++idx) { 135 | // if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 136 | // expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 137 | // } else { 138 | // expect(0n).toEqual(witness[2 + idx]); 139 | // } 140 | // } 141 | // }); 142 | 143 | it('dot2 invalid case 1', async () => { 144 | const inputStr = `819nc8b8`; 145 | const paddedStr = apis.padString(inputStr, 8); 146 | const circuitInputs = { 147 | msg: paddedStr 148 | }; 149 | const witness = await circuit2.calculateWitness(circuitInputs); 150 | await circuit2.checkConstraints(witness); 151 | expect(0n).toEqual(witness[1]); 152 | for (let idx = 0; idx < 8; ++idx) { 153 | expect(0n).toEqual(witness[2 + idx]); 154 | } 155 | }); 156 | 157 | it('dot2 invalid case 2', async () => { 158 | const inputStr = `78aa6cc8`; 159 | const paddedStr = apis.padString(inputStr, 8); 160 | const circuitInputs = { 161 | msg: paddedStr 162 | }; 163 | const witness = await circuit2.calculateWitness(circuitInputs); 164 | await circuit2.checkConstraints(witness); 165 | expect(0n).toEqual(witness[1]); 166 | for (let idx = 0; idx < 8; ++idx) { 167 | expect(0n).toEqual(witness[2 + idx]); 168 | } 169 | }); 170 | }); 171 | -------------------------------------------------------------------------------- /packages/circom/tests/email_addr.test.js: -------------------------------------------------------------------------------- 1 | import circom_tester from "circom_tester"; 2 | import * as path from "path"; 3 | import { readFileSync, writeFileSync } from "fs"; 4 | import apis from "../../apis/pkg"; 5 | import compiler from "../../compiler/pkg"; 6 | const option = { 7 | include: path.join(__dirname, "../../../node_modules"), 8 | }; 9 | const wasm_tester = circom_tester.wasm; 10 | 11 | jest.setTimeout(120000); 12 | describe("Email Address Regex", () => { 13 | let circuit; 14 | beforeAll(async () => { 15 | const email_addr_json = readFileSync( 16 | path.join(__dirname, "../circuits/common/email_addr.json"), 17 | "utf8" 18 | ); 19 | const circom = compiler.genFromDecomposed( 20 | email_addr_json, 21 | "EmailAddrRegex" 22 | ); 23 | writeFileSync( 24 | path.join(__dirname, "../circuits/common/email_addr_regex.circom"), 25 | circom 26 | ); 27 | circuit = await wasm_tester( 28 | path.join(__dirname, "./circuits/test_email_addr_regex.circom"), 29 | option 30 | ); 31 | }); 32 | 33 | it("only an email address", async () => { 34 | const emailAddr = "suegamisora@gmail.com"; 35 | const paddedStr = apis.padString(emailAddr, 256); 36 | const circuitInputs = { 37 | msg: paddedStr, 38 | }; 39 | const witness = await circuit.calculateWitness(circuitInputs); 40 | await circuit.checkConstraints(witness); 41 | expect(1n).toEqual(witness[1]); 42 | const prefixIdxes = apis.extractEmailAddrIdxes(emailAddr)[0]; 43 | for (let idx = 0; idx < 256; ++idx) { 44 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 45 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 46 | } else { 47 | expect(0n).toEqual(witness[2 + idx]); 48 | } 49 | } 50 | }); 51 | 52 | it("with a prefix", async () => { 53 | const prefix = "subject:"; 54 | const emailAddr = "suegamisora@gmail.com"; 55 | const string = prefix + emailAddr; 56 | const paddedStr = apis.padString(string, 256); 57 | const circuitInputs = { 58 | msg: paddedStr, 59 | }; 60 | const witness = await circuit.calculateWitness(circuitInputs); 61 | await circuit.checkConstraints(witness); 62 | expect(1n).toEqual(witness[1]); 63 | const prefixIdxes = apis.extractEmailAddrIdxes(string)[0]; 64 | for (let idx = 0; idx < 256; ++idx) { 65 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 66 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 67 | } else { 68 | expect(0n).toEqual(witness[2 + idx]); 69 | } 70 | } 71 | }); 72 | 73 | it("@ inside the name part", async () => { 74 | const prefix = "subject:"; 75 | const emailAddr = "suegamisora@gmail.com@dummy.com"; 76 | const string = prefix + emailAddr; 77 | const paddedStr = apis.padString(string, 256); 78 | const circuitInputs = { 79 | msg: paddedStr, 80 | }; 81 | const witness = await circuit.calculateWitness(circuitInputs); 82 | await circuit.checkConstraints(witness); 83 | expect(1n).toEqual(witness[1]); 84 | const prefixIdxes = apis.extractEmailAddrIdxes(string)[0]; 85 | for (let idx = 0; idx < 256; ++idx) { 86 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 87 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 88 | } else { 89 | expect(0n).toEqual(witness[2 + idx]); 90 | } 91 | } 92 | }); 93 | 94 | it("starts from @", async () => { 95 | const prefix = "subject:"; 96 | const emailAddr = "@gmail.com@dummy.com"; 97 | const string = prefix + emailAddr; 98 | const paddedStr = apis.padString(string, 256); 99 | const circuitInputs = { 100 | msg: paddedStr, 101 | }; 102 | const witness = await circuit.calculateWitness(circuitInputs); 103 | await circuit.checkConstraints(witness); 104 | expect(1n).toEqual(witness[1]); 105 | const prefixIdxes = apis.extractEmailAddrIdxes(string)[0]; 106 | for (let idx = 0; idx < 256; ++idx) { 107 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 108 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 109 | } else { 110 | expect(0n).toEqual(witness[2 + idx]); 111 | } 112 | } 113 | }); 114 | }); 115 | -------------------------------------------------------------------------------- /packages/circom/tests/email_domain.test.js: -------------------------------------------------------------------------------- 1 | import circom_tester from "circom_tester"; 2 | import * as path from "path"; 3 | import { readFileSync, writeFileSync } from "fs"; 4 | import apis from "../../apis/pkg"; 5 | import compiler from "../../compiler/pkg"; 6 | const option = { 7 | include: path.join(__dirname, "../../../node_modules"), 8 | }; 9 | const wasm_tester = circom_tester.wasm; 10 | 11 | jest.setTimeout(120000); 12 | describe("Email Domain Regex", () => { 13 | let circuit; 14 | beforeAll(async () => { 15 | const email_addr_json = readFileSync( 16 | path.join(__dirname, "../circuits/common/email_domain.json"), 17 | "utf8" 18 | ); 19 | const circom = compiler.genFromDecomposed( 20 | email_addr_json, 21 | "EmailDomainRegex" 22 | ); 23 | writeFileSync( 24 | path.join(__dirname, "../circuits/common/email_domain_regex.circom"), 25 | circom 26 | ); 27 | circuit = await wasm_tester( 28 | path.join(__dirname, "./circuits/test_email_domain_regex.circom"), 29 | option 30 | ); 31 | }); 32 | 33 | it("test a regex of an email domain", async () => { 34 | const emailAddr = "suegamisora@gmail.com"; 35 | const paddedStr = apis.padString(emailAddr, 256); 36 | const circuitInputs = { 37 | msg: paddedStr, 38 | }; 39 | const witness = await circuit.calculateWitness(circuitInputs); 40 | await circuit.checkConstraints(witness); 41 | expect(1n).toEqual(witness[1]); 42 | const prefixIdxes = apis.extractEmailDomainIdxes(emailAddr)[0]; 43 | for (let idx = 0; idx < 256; ++idx) { 44 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 45 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 46 | } else { 47 | expect(0n).toEqual(witness[2 + idx]); 48 | } 49 | } 50 | }); 51 | 52 | it("@ inside the name part", async () => { 53 | const emailAddr = "suegamisora@gmail.com@dummy.com"; 54 | const paddedStr = apis.padString(emailAddr, 256); 55 | const circuitInputs = { 56 | msg: paddedStr, 57 | }; 58 | const witness = await circuit.calculateWitness(circuitInputs); 59 | await circuit.checkConstraints(witness); 60 | expect(1n).toEqual(witness[1]); 61 | const prefixIdxes = apis.extractEmailDomainIdxes(emailAddr)[0]; 62 | expect("gmail.com@dummy.com").toEqual(emailAddr.slice(prefixIdxes[0], prefixIdxes[1])); 63 | for (let idx = 0; idx < 256; ++idx) { 64 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 65 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 66 | } else { 67 | expect(0n).toEqual(witness[2 + idx]); 68 | } 69 | } 70 | }); 71 | 72 | it("starts from @", async () => { 73 | const emailAddr = "@gmail.com@dummy.com"; 74 | const paddedStr = apis.padString(emailAddr, 256); 75 | const circuitInputs = { 76 | msg: paddedStr, 77 | }; 78 | const witness = await circuit.calculateWitness(circuitInputs); 79 | await circuit.checkConstraints(witness); 80 | expect(1n).toEqual(witness[1]); 81 | const prefixIdxes = apis.extractEmailDomainIdxes(emailAddr)[0]; 82 | expect("dummy.com").toEqual(emailAddr.slice(prefixIdxes[0], prefixIdxes[1])); 83 | for (let idx = 0; idx < 256; ++idx) { 84 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 85 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 86 | } else { 87 | expect(0n).toEqual(witness[2 + idx]); 88 | } 89 | } 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /packages/circom/tests/international_chars.test.js: -------------------------------------------------------------------------------- 1 | import circom_tester from "circom_tester"; 2 | import * as path from "path"; 3 | import { readFileSync, writeFileSync } from "fs"; 4 | import apis from "../../apis/pkg"; 5 | import compiler from "../../compiler/pkg"; 6 | const option = { 7 | include: path.join(__dirname, "../../../node_modules"), 8 | }; 9 | const wasm_tester = circom_tester.wasm; 10 | 11 | jest.setTimeout(300000); 12 | describe("Simple Regex Decomposed", () => { 13 | let circuit; 14 | beforeAll(async () => { 15 | const email_addr_json = readFileSync( 16 | path.join(__dirname, "./circuits/international_chars_decomposed.json"), 17 | "utf8" 18 | ); 19 | const circom = compiler.genFromDecomposed( 20 | email_addr_json, 21 | "InternationalCharsDecomposed" 22 | ); 23 | writeFileSync( 24 | path.join(__dirname, "./circuits/international_chars_decomposed.circom"), 25 | circom 26 | ); 27 | circuit = await wasm_tester( 28 | path.join( 29 | __dirname, 30 | "./circuits/test_international_chars_decomposed.circom" 31 | ), 32 | option 33 | ); 34 | }); 35 | 36 | it("case 1", async () => { 37 | const input = 38 | "Latin-Extension=Ʃƣƙ Greek=ϕω Cyrillic=иЩ Arabic=أبت Devanagari=आदित्य Hiragana&Katakana=なツ"; 39 | const paddedStr = apis.padString(input, 128); 40 | const circuitInputs = { 41 | msg: paddedStr, 42 | }; 43 | const witness = await circuit.calculateWitness(circuitInputs); 44 | await circuit.checkConstraints(witness); 45 | expect(1n).toEqual(witness[1]); 46 | const revealedIdx = [ 47 | [16, 17, 18, 19, 20, 21], 48 | [29, 30, 31, 32], 49 | [43, 44, 45, 46], 50 | [55, 56, 57, 58, 59, 60, 61, 62], 51 | [75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92], 52 | [112, 113, 114, 115, 116, 117], 53 | ]; 54 | for (let substr_idx = 0; substr_idx < 6; ++substr_idx) { 55 | for (let idx = 0; idx < 128; ++idx) { 56 | if (revealedIdx[substr_idx].includes(idx)) { 57 | expect(BigInt(paddedStr[idx])).toEqual( 58 | witness[2 + 128 * substr_idx + idx] 59 | ); 60 | } else { 61 | expect(0n).toEqual(witness[2 + 128 * substr_idx + idx]); 62 | } 63 | } 64 | } 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /packages/circom/tests/invitation_code.test.js: -------------------------------------------------------------------------------- 1 | import circom_tester from "circom_tester"; 2 | import * as path from "path"; 3 | import { readFileSync, writeFileSync } from "fs"; 4 | import apis from "../../apis/pkg"; 5 | import compiler from "../../compiler/pkg"; 6 | const option = { 7 | include: path.join(__dirname, "../../../node_modules"), 8 | }; 9 | const wasm_tester = circom_tester.wasm; 10 | 11 | jest.setTimeout(300000); 12 | describe("Invitation Code Decomposed (taken from ether-email-auth)", () => { 13 | let circuit; 14 | beforeAll(async () => { 15 | const email_addr_json = readFileSync( 16 | path.join(__dirname, "./circuits/invitation_code_with_prefix.json"), 17 | "utf8" 18 | ); 19 | const circom = compiler.genFromDecomposed( 20 | email_addr_json, 21 | "InvitationCodeWithPrefixRegex" 22 | ); 23 | writeFileSync( 24 | path.join(__dirname, "./circuits/invitation_code_with_prefix_regex.circom"), 25 | circom 26 | ); 27 | circuit = await wasm_tester( 28 | path.join( 29 | __dirname, 30 | "./circuits/test_invitation_code_with_prefix_regex.circom" 31 | ), 32 | option 33 | ); 34 | }); 35 | 36 | it("case 1", async () => { 37 | const input = 38 | "Re: Accept guardian request for 0x04884491560f38342C56E26BDD0fEAbb68E2d2FC Code 01eb9b204cc24c3baee11accc37d253a9c53e92b1a2cc07763475c135d575b76"; 39 | const paddedStr = apis.padString(input, 256); 40 | const circuitInputs = { 41 | msg: paddedStr, 42 | }; 43 | const witness = await circuit.calculateWitness(circuitInputs); 44 | await circuit.checkConstraints(witness); 45 | expect(1n).toEqual(witness[1]); 46 | const revealedIdx = [ 47 | [74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143], 48 | ]; 49 | for (let substr_idx = 0; substr_idx < 1; ++substr_idx) { 50 | for (let idx = 0; idx < 256; ++idx) { 51 | if (revealedIdx[substr_idx].includes(idx)) { 52 | expect(BigInt(paddedStr[idx])).toEqual( 53 | witness[2 + 256 * substr_idx + idx] 54 | ); 55 | } else { 56 | expect(0n).toEqual(witness[2 + 256 * substr_idx + idx]); 57 | } 58 | } 59 | } 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /packages/circom/tests/message_id_regex.test.js: -------------------------------------------------------------------------------- 1 | import circom_tester from "circom_tester"; 2 | import * as path from "path"; 3 | import { readFileSync, writeFileSync } from "fs"; 4 | import apis from "../../apis/pkg"; 5 | import compiler from "../../compiler/pkg"; 6 | const option = { 7 | include: path.join(__dirname, "../../../node_modules"), 8 | }; 9 | const wasm_tester = circom_tester.wasm; 10 | 11 | jest.setTimeout(120000); 12 | describe("Message Id Regex", () => { 13 | let circuit; 14 | beforeAll(async () => { 15 | const email_addr_json = readFileSync( 16 | path.join(__dirname, "../circuits/common/message_id.json"), 17 | "utf8" 18 | ); 19 | const circom = compiler.genFromDecomposed( 20 | email_addr_json, 21 | "MessageIdRegex" 22 | ); 23 | writeFileSync( 24 | path.join(__dirname, "../circuits/common/message_id_regex.circom"), 25 | circom 26 | ); 27 | circuit = await wasm_tester( 28 | path.join(__dirname, "./circuits/test_message_id_regex.circom"), 29 | option 30 | ); 31 | }); 32 | 33 | it("message id from beginning", async () => { 34 | const messageIdStr = `message-id:\r\n`; 35 | const paddedStr = apis.padString(messageIdStr, 256); 36 | const circuitInputs = { 37 | msg: paddedStr, 38 | }; 39 | const witness = await circuit.calculateWitness(circuitInputs); 40 | await circuit.checkConstraints(witness); 41 | expect(1n).toEqual(witness[1]); 42 | const prefixIdxes = apis.extractMessageIdIdxes(messageIdStr)[0]; 43 | for (let idx = 0; idx < 256; ++idx) { 44 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 45 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 46 | } else { 47 | expect(0n).toEqual(witness[2 + idx]); 48 | } 49 | } 50 | }); 51 | 52 | it("message id after new line", async () => { 53 | const messageIdStr = 54 | "dummy\r\nmessage-id:\r\n"; 55 | const paddedStr = apis.padString(messageIdStr, 256); 56 | const circuitInputs = { 57 | msg: paddedStr, 58 | }; 59 | const witness = await circuit.calculateWitness(circuitInputs); 60 | await circuit.checkConstraints(witness); 61 | expect(1n).toEqual(witness[1]); 62 | const prefixIdxes = apis.extractMessageIdIdxes(messageIdStr)[0]; 63 | for (let idx = 0; idx < 256; ++idx) { 64 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 65 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 66 | } else { 67 | expect(0n).toEqual(witness[2 + idx]); 68 | } 69 | } 70 | }); 71 | 72 | it("invalid message id", async () => { 73 | const messageIdStr = `to:message-id:\r\n`; 74 | const paddedStr = apis.padString(messageIdStr, 256); 75 | const circuitInputs = { 76 | msg: paddedStr, 77 | }; 78 | const witness = await circuit.calculateWitness(circuitInputs); 79 | await circuit.checkConstraints(witness); 80 | expect(0n).toEqual(witness[1]); 81 | for (let idx = 0; idx < 256; ++idx) { 82 | expect(0n).toEqual(witness[2 + idx]); 83 | } 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /packages/circom/tests/negate_regex.test.js: -------------------------------------------------------------------------------- 1 | import circom_tester from "circom_tester"; 2 | import * as path from "path"; 3 | import { readFileSync, writeFileSync } from "fs"; 4 | import apis from "../../apis/pkg"; 5 | import compiler from "../../compiler/pkg"; 6 | const option = { 7 | include: path.join(__dirname, "../../../node_modules"), 8 | }; 9 | const wasm_tester = circom_tester.wasm; 10 | 11 | jest.setTimeout(120000); 12 | describe("Negate Regex", () => { 13 | let circuit1; 14 | let circuit2; 15 | beforeAll(async () => { 16 | writeFileSync( 17 | path.join(__dirname, "./circuits/negate1_regex.circom"), 18 | compiler.genFromDecomposed( 19 | readFileSync( 20 | path.join(__dirname, "./circuits/negate1.json"), 21 | "utf8" 22 | ), 23 | "Negate1Regex" 24 | ) 25 | ); 26 | circuit1 = await wasm_tester( 27 | path.join(__dirname, "./circuits/test_negate1_regex.circom"), 28 | option 29 | ); 30 | writeFileSync( 31 | path.join(__dirname, "./circuits/negate2_regex.circom"), 32 | compiler.genFromDecomposed( 33 | readFileSync( 34 | path.join(__dirname, "./circuits/negate2.json"), 35 | "utf8" 36 | ), 37 | "Negate2Regex" 38 | ) 39 | ); 40 | circuit2 = await wasm_tester( 41 | path.join(__dirname, "./circuits/test_negate2_regex.circom"), 42 | option 43 | ); 44 | }); 45 | 46 | it("case 1 with regex 1", async () => { 47 | const input = "a: ABCDEFG XYZ."; 48 | const paddedStr = apis.padString(input, 64); 49 | const circuitInputs = { 50 | msg: paddedStr, 51 | }; 52 | const witness = await circuit1.calculateWitness(circuitInputs); 53 | await circuit1.checkConstraints(witness); 54 | expect(1n).toEqual(witness[1]); 55 | const revealedIdx = [[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]]; 56 | for (let substr_idx = 0; substr_idx < 1; ++substr_idx) { 57 | for (let idx = 0; idx < 64; ++idx) { 58 | if (revealedIdx[substr_idx].includes(idx)) { 59 | expect(BigInt(paddedStr[idx])).toEqual( 60 | witness[2 + 64 * substr_idx + idx] 61 | ); 62 | } else { 63 | expect(0n).toEqual(witness[2 + 64 * substr_idx + idx]); 64 | } 65 | } 66 | } 67 | }); 68 | 69 | it("case 2 with regex 1", async () => { 70 | const input = "a: CRIPTOGRAFíA."; 71 | const paddedStr = apis.padString(input, 64); 72 | const circuitInputs = { 73 | msg: paddedStr, 74 | }; 75 | const witness = await circuit1.calculateWitness(circuitInputs); 76 | await circuit1.checkConstraints(witness); 77 | expect(1n).toEqual(witness[1]); 78 | const revealedIdx = [[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]]; 79 | for (let substr_idx = 0; substr_idx < 1; ++substr_idx) { 80 | for (let idx = 0; idx < 64; ++idx) { 81 | if (revealedIdx[substr_idx].includes(idx)) { 82 | expect(BigInt(paddedStr[idx])).toEqual( 83 | witness[2 + 64 * substr_idx + idx] 84 | ); 85 | } else { 86 | expect(0n).toEqual(witness[2 + 64 * substr_idx + idx]); 87 | } 88 | } 89 | } 90 | }); 91 | 92 | it("case 3 with regex 1", async () => { 93 | const input = "a: あいう."; 94 | const paddedStr = apis.padString(input, 64); 95 | const circuitInputs = { 96 | msg: paddedStr, 97 | }; 98 | const witness = await circuit1.calculateWitness(circuitInputs); 99 | await circuit1.checkConstraints(witness); 100 | expect(1n).toEqual(witness[1]); 101 | const revealedIdx = [[2, 3, 4, 5, 6, 7, 8, 9, 10, 11]]; 102 | for (let substr_idx = 0; substr_idx < 1; ++substr_idx) { 103 | for (let idx = 0; idx < 64; ++idx) { 104 | if (revealedIdx[substr_idx].includes(idx)) { 105 | expect(BigInt(paddedStr[idx])).toEqual( 106 | witness[2 + 64 * substr_idx + idx] 107 | ); 108 | } else { 109 | expect(0n).toEqual(witness[2 + 64 * substr_idx + idx]); 110 | } 111 | } 112 | } 113 | }); 114 | 115 | it("case 4 with regex 1", async () => { 116 | const input = "a: التشفير."; 117 | const paddedStr = apis.padString(input, 64); 118 | const circuitInputs = { 119 | msg: paddedStr, 120 | }; 121 | const witness = await circuit1.calculateWitness(circuitInputs); 122 | await circuit1.checkConstraints(witness); 123 | expect(1n).toEqual(witness[1]); 124 | const revealedIdx = [[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]]; 125 | for (let substr_idx = 0; substr_idx < 1; ++substr_idx) { 126 | for (let idx = 0; idx < 64; ++idx) { 127 | if (revealedIdx[substr_idx].includes(idx)) { 128 | expect(BigInt(paddedStr[idx])).toEqual( 129 | witness[2 + 64 * substr_idx + idx] 130 | ); 131 | } else { 132 | expect(0n).toEqual(witness[2 + 64 * substr_idx + idx]); 133 | } 134 | } 135 | } 136 | }); 137 | 138 | it("case 1 with regex 2", async () => { 139 | const input = "abdefia"; 140 | const paddedStr = apis.padString(input, 64); 141 | const circuitInputs = { 142 | msg: paddedStr, 143 | }; 144 | const witness = await circuit2.calculateWitness(circuitInputs); 145 | await circuit2.checkConstraints(witness); 146 | expect(1n).toEqual(witness[1]); 147 | const revealedIdx = [2,3,4,5]; 148 | for (let idx = 0; idx < 64; ++idx) { 149 | if (revealedIdx.includes(idx)) { 150 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 151 | } else { 152 | expect(0n).toEqual(witness[2 + idx]); 153 | } 154 | } 155 | }); 156 | 157 | it("invalid case 1 with regex 2", async () => { 158 | const input = "a"; 159 | const paddedStr = apis.padString(input, 64); 160 | const circuitInputs = { 161 | msg: paddedStr, 162 | }; 163 | const witness = await circuit2.calculateWitness(circuitInputs); 164 | await circuit2.checkConstraints(witness); 165 | expect(0n).toEqual(witness[1]); 166 | for (let idx = 0; idx < 64; ++idx) { 167 | expect(0n).toEqual(witness[2 + idx]); 168 | } 169 | }); 170 | }); 171 | -------------------------------------------------------------------------------- /packages/circom/tests/simple_regex.test.js: -------------------------------------------------------------------------------- 1 | import circom_tester from "circom_tester"; 2 | import * as path from "path"; 3 | import { readFileSync, writeFileSync } from "fs"; 4 | import apis from "../../apis/pkg"; 5 | import compiler from "../../compiler/pkg"; 6 | const option = { 7 | include: path.join(__dirname, "../../../node_modules"), 8 | }; 9 | const wasm_tester = circom_tester.wasm; 10 | 11 | jest.setTimeout(120000); 12 | describe("Simple Regex", () => { 13 | let circuit; 14 | beforeAll(async () => { 15 | const substrs_json = readFileSync( 16 | path.join(__dirname, "./circuits/simple_regex_substrs.json"), 17 | "utf8" 18 | ); 19 | const circom = compiler.genFromRaw( 20 | "1=(a|b) (2=(b|c)+ )+d", 21 | substrs_json, 22 | "SimpleRegex" 23 | ); 24 | writeFileSync( 25 | path.join(__dirname, "./circuits/simple_regex.circom"), 26 | circom 27 | ); 28 | circuit = await wasm_tester( 29 | path.join(__dirname, "./circuits/test_simple_regex.circom"), 30 | option 31 | ); 32 | }); 33 | 34 | it("case 1", async () => { 35 | const input = "1=a 2=b d"; 36 | const paddedStr = apis.padString(input, 64); 37 | const circuitInputs = { 38 | msg: paddedStr, 39 | }; 40 | const witness = await circuit.calculateWitness(circuitInputs); 41 | await circuit.checkConstraints(witness); 42 | expect(1n).toEqual(witness[1]); 43 | const revealedIdx = [[2], [6], [8]]; 44 | for (let substr_idx = 0; substr_idx < 3; ++substr_idx) { 45 | for (let idx = 0; idx < 64; ++idx) { 46 | if (revealedIdx[substr_idx].includes(idx)) { 47 | expect(BigInt(paddedStr[idx])).toEqual( 48 | witness[2 + 64 * substr_idx + idx] 49 | ); 50 | } else { 51 | expect(0n).toEqual(witness[2 + 64 * substr_idx + idx]); 52 | } 53 | } 54 | } 55 | }); 56 | 57 | it("case 2", async () => { 58 | const input = "1=a 2=b 2=bc 2=c d"; 59 | const paddedStr = apis.padString(input, 64); 60 | const circuitInputs = { 61 | msg: paddedStr, 62 | }; 63 | const witness = await circuit.calculateWitness(circuitInputs); 64 | await circuit.checkConstraints(witness); 65 | expect(1n).toEqual(witness[1]); 66 | const revealedIdx = [[2], [6, 10, 11, 15], [17]]; 67 | for (let substr_idx = 0; substr_idx < 3; ++substr_idx) { 68 | for (let idx = 0; idx < 64; ++idx) { 69 | if (revealedIdx[substr_idx].includes(idx)) { 70 | expect(BigInt(paddedStr[idx])).toEqual( 71 | witness[2 + 64 * substr_idx + idx] 72 | ); 73 | } else { 74 | expect(0n).toEqual(witness[2 + 64 * substr_idx + idx]); 75 | } 76 | } 77 | } 78 | }); 79 | 80 | it("case 3", async () => { 81 | const input = "1=a 2=b 2=bc 2=c da 1=a 2=cb 2=c 2=b dd"; 82 | const paddedStr = apis.padString(input, 64); 83 | const circuitInputs = { 84 | msg: paddedStr, 85 | }; 86 | 87 | const witness = await circuit.calculateWitness(circuitInputs); 88 | await circuit.checkConstraints(witness); 89 | expect(1n).toEqual(witness[1]); 90 | const revealedIdx = [ 91 | [2, 22], 92 | [6, 10, 11, 15, 26, 27, 31, 35], 93 | [17, 37], 94 | ]; 95 | for (let substr_idx = 0; substr_idx < 3; ++substr_idx) { 96 | for (let idx = 0; idx < 64; ++idx) { 97 | if (revealedIdx[substr_idx].includes(idx)) { 98 | expect(BigInt(paddedStr[idx])).toEqual( 99 | witness[2 + 64 * substr_idx + idx] 100 | ); 101 | } else { 102 | expect(0n).toEqual(witness[2 + 64 * substr_idx + idx]); 103 | } 104 | } 105 | } 106 | }); 107 | }); 108 | -------------------------------------------------------------------------------- /packages/circom/tests/simple_regex_decomposed.test.js: -------------------------------------------------------------------------------- 1 | import circom_tester from "circom_tester"; 2 | import * as path from "path"; 3 | import { readFileSync, writeFileSync } from "fs"; 4 | import apis from "../../apis/pkg"; 5 | import compiler from "../../compiler/pkg"; 6 | const option = { 7 | include: path.join(__dirname, "../../../node_modules"), 8 | }; 9 | const wasm_tester = circom_tester.wasm; 10 | 11 | jest.setTimeout(120000); 12 | describe("Simple Regex Decomposed", () => { 13 | let circuit; 14 | beforeAll(async () => { 15 | const email_addr_json = readFileSync( 16 | path.join(__dirname, "./circuits/simple_regex_decomposed.json"), 17 | "utf8" 18 | ); 19 | const circom = compiler.genFromDecomposed( 20 | email_addr_json, 21 | "SimpleRegexDecomposed" 22 | ); 23 | writeFileSync( 24 | path.join(__dirname, "./circuits/simple_regex_decomposed.circom"), 25 | circom 26 | ); 27 | circuit = await wasm_tester( 28 | path.join(__dirname, "./circuits/test_simple_regex_decomposed.circom"), 29 | option 30 | ); 31 | }); 32 | 33 | it("case 1", async () => { 34 | const input = "email was meant for @zkRegex."; 35 | const paddedStr = apis.padString(input, 64); 36 | const circuitInputs = { 37 | msg: paddedStr, 38 | }; 39 | const witness = await circuit.calculateWitness(circuitInputs); 40 | await circuit.checkConstraints(witness); 41 | expect(1n).toEqual(witness[1]); 42 | const revealedIdx = [[21, 22, 23, 24, 25, 26, 27]]; 43 | for (let substr_idx = 0; substr_idx < 1; ++substr_idx) { 44 | for (let idx = 0; idx < 64; ++idx) { 45 | if (revealedIdx[substr_idx].includes(idx)) { 46 | expect(BigInt(paddedStr[idx])).toEqual( 47 | witness[2 + 64 * substr_idx + idx] 48 | ); 49 | } else { 50 | expect(0n).toEqual(witness[2 + 64 * substr_idx + idx]); 51 | } 52 | } 53 | } 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /packages/circom/tests/subject_all.test.js: -------------------------------------------------------------------------------- 1 | import circom_tester from "circom_tester"; 2 | import * as path from "path"; 3 | import { readFileSync, writeFileSync } from "fs"; 4 | import apis from "../../apis/pkg"; 5 | import compiler from "../../compiler/pkg"; 6 | const option = { 7 | include: path.join(__dirname, "../../../node_modules"), 8 | }; 9 | const wasm_tester = circom_tester.wasm; 10 | 11 | jest.setTimeout(120000); 12 | describe("Subject All Regex", () => { 13 | let circuit; 14 | beforeAll(async () => { 15 | const email_addr_json = readFileSync( 16 | path.join(__dirname, "../circuits/common/subject_all.json"), 17 | "utf8" 18 | ); 19 | const circom = compiler.genFromDecomposed( 20 | email_addr_json, 21 | "SubjectAllRegex" 22 | ); 23 | writeFileSync( 24 | path.join(__dirname, "../circuits/common/subject_all_regex.circom"), 25 | circom 26 | ); 27 | circuit = await wasm_tester( 28 | path.join(__dirname, "./circuits/test_subject_all_regex.circom"), 29 | option 30 | ); 31 | }); 32 | 33 | it("subject from beginning", async () => { 34 | const subjectStr = "subject:This is a test.\r\n"; 35 | const paddedStr = apis.padString(subjectStr, 256); 36 | const circuitInputs = { 37 | msg: paddedStr, 38 | }; 39 | const witness = await circuit.calculateWitness(circuitInputs); 40 | await circuit.checkConstraints(witness); 41 | expect(1n).toEqual(witness[1]); 42 | const prefixIdxes = apis.extractSubjectAllIdxes(subjectStr)[0]; 43 | for (let idx = 0; idx < 256; ++idx) { 44 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 45 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 46 | } else { 47 | expect(0n).toEqual(witness[2 + idx]); 48 | } 49 | } 50 | }); 51 | 52 | it("subject after new line", async () => { 53 | const subjectStr = "dummy\r\nsubject:This is a test.\r\n"; 54 | const paddedStr = apis.padString(subjectStr, 256); 55 | const circuitInputs = { 56 | msg: paddedStr, 57 | }; 58 | const witness = await circuit.calculateWitness(circuitInputs); 59 | await circuit.checkConstraints(witness); 60 | expect(1n).toEqual(witness[1]); 61 | const prefixIdxes = apis.extractSubjectAllIdxes(subjectStr)[0]; 62 | for (let idx = 0; idx < 256; ++idx) { 63 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 64 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 65 | } else { 66 | expect(0n).toEqual(witness[2 + idx]); 67 | } 68 | } 69 | }); 70 | 71 | it("subject from beginning and non-English case", async () => { 72 | const subjectStr = "subject:これはテストです。\r\n"; 73 | const paddedStr = apis.padString(subjectStr, 256); 74 | const circuitInputs = { 75 | msg: paddedStr, 76 | }; 77 | const witness = await circuit.calculateWitness(circuitInputs); 78 | await circuit.checkConstraints(witness); 79 | expect(1n).toEqual(witness[1]); 80 | const prefixIdxes = apis.extractSubjectAllIdxes(subjectStr)[0]; 81 | for (let idx = 0; idx < 256; ++idx) { 82 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 83 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 84 | } else { 85 | expect(0n).toEqual(witness[2 + idx]); 86 | } 87 | } 88 | }); 89 | 90 | it("subject in the invalid field", async () => { 91 | const subjectStr = "\r\nto:subject:This is a subject in To field.\r\n"; 92 | const paddedStr = apis.padString(subjectStr, 256); 93 | const circuitInputs = { 94 | msg: paddedStr, 95 | }; 96 | const witness = await circuit.calculateWitness(circuitInputs); 97 | await circuit.checkConstraints(witness); 98 | expect(0n).toEqual(witness[1]); 99 | for (let idx = 0; idx < 256; ++idx) { 100 | expect(0n).toEqual(witness[2 + idx]); 101 | } 102 | }); 103 | 104 | it('invalid subject field with 255', async () => { 105 | const subjectStr = `subject:This is a subject in To field.\r\n`; 106 | let paddedStr = apis.padString(subjectStr, 1022); 107 | paddedStr.unshift(49); 108 | paddedStr.unshift(255); 109 | const circuitInputs = { 110 | msg: paddedStr 111 | }; 112 | async function failFn() { 113 | const witness = await circuit.calculateWitness(circuitInputs); 114 | await circuit.checkConstraints(witness); 115 | } 116 | await expect(failFn).rejects.toThrow(); 117 | }); 118 | }); 119 | -------------------------------------------------------------------------------- /packages/circom/tests/timestamp.test.js: -------------------------------------------------------------------------------- 1 | import circom_tester from "circom_tester"; 2 | import * as path from "path"; 3 | import { readFileSync, writeFileSync } from "fs"; 4 | import apis from "../../apis/pkg"; 5 | import compiler from "../../compiler/pkg"; 6 | const option = { 7 | include: path.join(__dirname, "../../../node_modules"), 8 | }; 9 | const wasm_tester = circom_tester.wasm; 10 | 11 | jest.setTimeout(600000); 12 | describe("Timestamp Regex", () => { 13 | let circuit; 14 | beforeAll(async () => { 15 | const email_addr_json = readFileSync( 16 | path.join(__dirname, "../circuits/common/timestamp.json"), 17 | "utf8" 18 | ); 19 | const circom = compiler.genFromDecomposed( 20 | email_addr_json, 21 | "TimestampRegex" 22 | ); 23 | writeFileSync( 24 | path.join(__dirname, "../circuits/common/timestamp_regex.circom"), 25 | circom 26 | ); 27 | circuit = await wasm_tester( 28 | path.join(__dirname, "./circuits/test_timestamp_regex.circom"), 29 | option 30 | ); 31 | }); 32 | 33 | it("timestamp in the header", async () => { 34 | const signatureField = `dkim-signature:v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1694989812; x=1695594612; dara=google.com; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=BWETwQ9JDReS4GyR2v2TTR8Bpzj9ayumsWQJ3q7vehs=; b=`; 35 | const paddedStr = apis.padString(signatureField, 1024); 36 | const circuitInputs = { 37 | msg: paddedStr, 38 | }; 39 | const witness = await circuit.calculateWitness(circuitInputs); 40 | await circuit.checkConstraints(witness); 41 | expect(1n).toEqual(witness[1]); 42 | const prefixIdxes = apis.extractTimestampIdxes(signatureField)[0]; 43 | for (let idx = 0; idx < 1024; ++idx) { 44 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 45 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 46 | } else { 47 | expect(0n).toEqual(witness[2 + idx]); 48 | } 49 | } 50 | }); 51 | 52 | it("timestamp after new line", async () => { 53 | const signatureField = `\r\ndkim-signature:v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1694989812; x=1695594612; dara=google.com; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=BWETwQ9JDReS4GyR2v2TTR8Bpzj9ayumsWQJ3q7vehs=; b=`; 54 | const paddedStr = apis.padString(signatureField, 1024); 55 | const circuitInputs = { 56 | msg: paddedStr, 57 | }; 58 | const witness = await circuit.calculateWitness(circuitInputs); 59 | await circuit.checkConstraints(witness); 60 | expect(1n).toEqual(witness[1]); 61 | const prefixIdxes = apis.extractTimestampIdxes(signatureField)[0]; 62 | for (let idx = 0; idx < 1024; ++idx) { 63 | if (idx >= prefixIdxes[0] && idx < prefixIdxes[1]) { 64 | expect(BigInt(paddedStr[idx])).toEqual(witness[2 + idx]); 65 | } else { 66 | expect(0n).toEqual(witness[2 + idx]); 67 | } 68 | } 69 | }); 70 | 71 | 72 | it("invalid timestamp", async () => { 73 | const signatureField = `to:dkim-signature:v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1694989812; x=1695594612; dara=google.com; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=BWETwQ9JDReS4GyR2v2TTR8Bpzj9ayumsWQJ3q7vehs=; b=`; 74 | const paddedStr = apis.padString(signatureField, 1024); 75 | const circuitInputs = { 76 | msg: paddedStr, 77 | }; 78 | const witness = await circuit.calculateWitness(circuitInputs); 79 | await circuit.checkConstraints(witness); 80 | expect(0n).toEqual(witness[1]); 81 | for (let idx = 0; idx < 1024; ++idx) { 82 | expect(0n).toEqual(witness[2 + idx]); 83 | } 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /packages/compiler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zk-regex-compiler" 3 | version = "2.3.2" 4 | authors = [ 5 | "Javier Su ", 6 | "Kata Choi ", 7 | "Sora Suegami ", 8 | "Yush G ", 9 | "Aditya Bisht ", 10 | ] 11 | license = "MIT" 12 | edition = "2018" 13 | 14 | [[bin]] 15 | name = "zk-regex" 16 | path = "src/bin/compiler.rs" 17 | 18 | [lib] 19 | crate-type = ["rlib", "cdylib"] 20 | 21 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 22 | 23 | [dependencies] 24 | tabbycat = { version = "0.1", features = ["attributes"], optional = true } 25 | fancy-regex = "=0.13.0" 26 | petgraph = "0.6.3" 27 | graph-cycles = "0.1.0" 28 | thiserror = "1.0.40" 29 | serde_json = "1.0.95" 30 | serde = { version = "1.0.159", features = ["derive"] } 31 | itertools = "0.13.0" 32 | clap = { version = "=4.2.1", features = ["derive"] } 33 | ahash = "=0.8.11" 34 | regex-automata = "=0.4.7" 35 | regex = "=1.10.6" 36 | getrandom = { version = "0.2", features = ["js"] } 37 | wasm-bindgen = "0.2" 38 | serde-wasm-bindgen = "0.6.5" 39 | console_error_panic_hook = "0.1.7" 40 | -------------------------------------------------------------------------------- /packages/compiler/README.md: -------------------------------------------------------------------------------- 1 | # zk-regex-compiler 2 | 3 | A compiler CLI in [zk-regex](https://github.com/zkemail/zk-regex/tree/main). 4 | 5 | ## Installing zk-regex-compiler 6 | 7 | You can install the project with npm. In the project directory, run: 8 | 9 | ```sh 10 | $ npm install 11 | ``` 12 | 13 | This fully installs the project, including installing any dependencies and running the build. 14 | 15 | ## Building zk-regex-compiler 16 | 17 | If you have already installed the project and only want to run the build, run: 18 | 19 | ```sh 20 | $ npm run build 21 | ``` 22 | 23 | ## Compiling zk-regex-compiler to wasm 24 | 25 | ### For web usage 26 | Install `wasm-pack` if not already installed 27 | 28 | ```sh 29 | cargo install wasm-pack 30 | ``` 31 | 32 | Compile the web package 33 | 34 | ```sh 35 | wasm-pack build --target nodejs 36 | ``` 37 | 38 | Pack the package (optional) 39 | 40 | ```sh 41 | wasm-pack build --target nodejs 42 | cd pkg 43 | npm pkg set type='module' 44 | wasm-pack pack 45 | ``` 46 | 47 | The output package file will be `packages/compiler/pkg/zk-regex-compiler-1.1.1.tgz` 48 | 49 | ### For tests 50 | 51 | ```sh 52 | wasm-pack test --node 53 | ``` 54 | 55 | ## CLI Usage 56 | Please see "Compiler CLI" section in [zk-regex](https://github.com/zkemail/zk-regex/tree/main). 57 | 58 | ## Available Scripts 59 | 60 | In the project directory, you can run: 61 | 62 | ### `npm install` 63 | 64 | Installs the project, including running `npm run build`. 65 | 66 | ### `npm build` 67 | 68 | Additional [`cargo build`](https://doc.rust-lang.org/cargo/commands/cargo-build.html) arguments may be passed to `npm build` and `npm build-*` commands. For example, to enable a [cargo feature](https://doc.rust-lang.org/cargo/reference/features.html): 69 | 70 | ``` 71 | npm run build -- --feature=beetle 72 | ``` 73 | 74 | #### `npm build-debug` 75 | 76 | Alias for `npm build`. 77 | 78 | #### `npm build-release` 79 | 80 | Same as [`npm build`](#npm-build) but, builds the module with the [`release`](https://doc.rust-lang.org/cargo/reference/profiles.html#release) profile. Release builds will compile slower, but run faster. 81 | 82 | ### `npm test` 83 | 84 | Runs the unit tests by calling `wasm-pack test --node`. 85 | 86 | ## Project Layout 87 | 88 | The directory structure of this project is: 89 | 90 | ``` 91 | zk-regex-compiler/ 92 | ├── Cargo.toml 93 | ├── README.md 94 | ├── package.json 95 | ├── src/ 96 | | └── lib.rs 97 | └── target/ 98 | ``` 99 | 100 | ### Cargo.toml 101 | 102 | The Cargo [manifest file](https://doc.rust-lang.org/cargo/reference/manifest.html), which informs the `cargo` command. 103 | 104 | ### README.md 105 | 106 | This file. 107 | 108 | ### package.json 109 | 110 | The npm [manifest file](https://docs.npmjs.com/cli/v7/configuring-npm/package-json), which informs the `npm` command. 111 | 112 | ### src/ 113 | 114 | The directory tree containing the Rust source code for the project. 115 | 116 | ### src/lib.rs 117 | 118 | The Rust library's main module. 119 | 120 | ### target/ 121 | 122 | Binary artifacts generated by the Rust build. 123 | 124 | ## Regex Limitations 125 | The regular expressions supported by our compiler have the following limitations: 126 | 127 | - Regular expressions where the results differ between greedy and lazy matching (e.g., .+, .+?) are **not** supported. 128 | - The beginning anchor ^ must either appear at the beginning of the regular expression or be in the format (|^). Additionally, the section containing this ^ must be non-public (`is_public: false`). 129 | - The end anchor $ must appear at the end of the regular expression. 130 | - Regular expressions that, when converted to DFA (Deterministic Finite Automaton), include transitions to the initial state are **not** supported (e.g., .*). 131 | - Regular expressions that, when converted to DFA, have multiple accepting states are **not** supported. 132 | -------------------------------------------------------------------------------- /packages/compiler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zk-email/zk-regex-compiler", 3 | "version": "2.3.2", 4 | "description": "A compiler to generate a regex verification circuit in circom from a user-defined regex. Please check [zk-regex](https://github.com/zkemail/zk-regex/tree/main) for the detail.", 5 | "contributors": [ 6 | "Javier Su ", 7 | "Kata Choi ", 8 | "Sora Suegami ", 9 | "Yush G ", 10 | "Aditya Bisht " 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/zkemail/zk-regex.git" 15 | }, 16 | "scripts": { 17 | "build": "cargo build && wasm-pack build --target nodejs --out-dir ./pkg/", 18 | "build-debug": "npm run build --", 19 | "build-release": "npm run build -- --release", 20 | "install": "npm run build-release", 21 | "install-debug": "npm run build-debug", 22 | "test": "cargo test && wasm-pack test --node", 23 | "upload-binary": "wasm-pack publish -t nodejs" 24 | }, 25 | "license": "MIT" 26 | } -------------------------------------------------------------------------------- /packages/compiler/src/bin/compiler.rs: -------------------------------------------------------------------------------- 1 | //! ZK Regex Compiler CLI 2 | //! 3 | //! This binary provides a command-line interface for the ZK Regex Compiler. 4 | //! It supports two main commands: `Decomposed` for working with decomposed regex files, 5 | //! and `Raw` for working with raw regex strings. 6 | //! 7 | //! # Usage 8 | //! 9 | //! ## Decomposed Command 10 | //! Process a decomposed regex file: 11 | //! 12 | //! ``` 13 | //! zk-regex decomposed --decomposed-regex-path [OPTIONS] 14 | //! ``` 15 | //! 16 | //! Options: 17 | //! - `-d, --decomposed-regex-path `: Path to the decomposed regex JSON file (required) 18 | //! - `-h, --halo2-dir-path `: Directory path for Halo2 output 19 | //! - `-c, --circom-file-path `: File path for Circom output 20 | //! - `-t, --template-name `: Template name 21 | //! - `-g, --gen-substrs`: Generate substrings 22 | //! 23 | //! Example: 24 | //! ``` 25 | //! zk-regex decomposed -d regex.json -h ./halo2_output -c ./circom_output.circom -t MyTemplate -g true 26 | //! ``` 27 | //! 28 | //! ## Raw Command 29 | //! Process a raw regex string: 30 | //! 31 | //! ``` 32 | //! zk-regex raw --raw-regex [OPTIONS] 33 | //! ``` 34 | //! 35 | //! Options: 36 | //! - `-r, --raw-regex `: Raw regex string (required) 37 | //! - `-s, --substrs-json-path `: Path to substrings JSON file 38 | //! - `-h, --halo2-dir-path `: Directory path for Halo2 output 39 | //! - `-c, --circom-file-path `: File path for Circom output 40 | //! - `-t, --template-name `: Template name 41 | //! - `-g, --gen-substrs`: Generate substrings 42 | //! 43 | //! Example: 44 | //! ``` 45 | //! zk-regex raw -r "a*b+c?" -s substrings.json -h ./halo2_output -c ./circom_output.circom -t MyTemplate -g true 46 | //! ``` 47 | 48 | use clap::{Parser, Subcommand}; 49 | use zk_regex_compiler::{gen_from_decomposed, gen_from_raw}; 50 | 51 | #[derive(Parser, Debug, Clone)] 52 | #[command(author, version, about, long_about = None)] 53 | struct Cli { 54 | #[command(subcommand)] 55 | pub command: Commands, 56 | } 57 | 58 | #[derive(Debug, Subcommand, Clone)] 59 | enum Commands { 60 | Decomposed { 61 | #[arg(short, long)] 62 | decomposed_regex_path: String, 63 | #[arg(short, long)] 64 | halo2_dir_path: Option, 65 | #[arg(short, long)] 66 | circom_file_path: Option, 67 | #[arg(short, long)] 68 | template_name: Option, 69 | #[arg(short, long)] 70 | gen_substrs: Option, 71 | }, 72 | Raw { 73 | #[arg(short, long)] 74 | raw_regex: String, 75 | #[arg(short, long)] 76 | substrs_json_path: Option, 77 | #[arg(short, long)] 78 | halo2_dir_path: Option, 79 | #[arg(short, long)] 80 | circom_file_path: Option, 81 | #[arg(short, long)] 82 | template_name: Option, 83 | #[arg(short, long)] 84 | gen_substrs: Option, 85 | }, 86 | } 87 | 88 | fn main() { 89 | let cli = Cli::parse(); 90 | match cli.command { 91 | Commands::Decomposed { .. } => process_decomposed(cli), 92 | Commands::Raw { .. } => process_raw(cli), 93 | } 94 | } 95 | 96 | fn process_decomposed(cli: Cli) { 97 | if let Commands::Decomposed { 98 | decomposed_regex_path, 99 | halo2_dir_path, 100 | circom_file_path, 101 | template_name, 102 | gen_substrs, 103 | } = cli.command 104 | { 105 | if let Err(e) = gen_from_decomposed( 106 | &decomposed_regex_path, 107 | halo2_dir_path.as_deref(), 108 | circom_file_path.as_deref(), 109 | template_name.as_deref(), 110 | gen_substrs, 111 | ) { 112 | eprintln!("Error: {}", e); 113 | std::process::exit(1); 114 | } 115 | } 116 | } 117 | 118 | fn process_raw(cli: Cli) { 119 | if let Commands::Raw { 120 | raw_regex, 121 | substrs_json_path, 122 | halo2_dir_path, 123 | circom_file_path, 124 | template_name, 125 | gen_substrs, 126 | } = cli.command 127 | { 128 | if let Err(e) = gen_from_raw( 129 | &raw_regex, 130 | substrs_json_path.as_deref(), 131 | halo2_dir_path.as_deref(), 132 | circom_file_path.as_deref(), 133 | template_name.as_deref(), 134 | gen_substrs, 135 | ) { 136 | eprintln!("Error: {}", e); 137 | std::process::exit(1); 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /packages/compiler/src/dfa_tests.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "regex": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", 4 | "pass": ["user@example.com", "john.doe123@sub.domain.co.uk"], 5 | "fail": ["@example.com", "user@.com", "user@com", "user@example.c"] 6 | }, 7 | { 8 | "regex": "^\\d{3}-\\d{3}-\\d{4}$", 9 | "pass": ["123-456-7890", "000-000-0000"], 10 | "fail": ["123-45-6789", "12-345-6789", "123-456-789", "abc-def-ghij"] 11 | }, 12 | { 13 | "regex": "^(https?:\\/\\/)?([\\da-z\\.-]+)\\.([a-z\\.]{2,6})([\\/\\w \\.-]*)*\\/?$", 14 | "pass": [ 15 | "http://example.com", 16 | "https://sub.domain.co.uk/page", 17 | "www.example.com" 18 | ], 19 | "fail": ["htp://invalid", "http://.com", "https://example."] 20 | }, 21 | { 22 | "regex": "^[0-9]{5}(-[0-9]{4})?$", 23 | "pass": ["12345", "12345-6789"], 24 | "fail": ["1234", "123456", "12345-", "12345-67890"] 25 | }, 26 | { 27 | "regex": "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", 28 | "pass": ["#123abc", "#FFF", "#000000"], 29 | "fail": ["123abc", "#GGGGGG", "#FFG", "#F0F0F0F"] 30 | }, 31 | { 32 | "regex": "^([01]?[0-9]|2[0-3]):[0-5][0-9]$", 33 | "pass": ["00:00", "23:59", "1:23", "12:34"], 34 | "fail": ["24:00", "12:60", "1:2", "00:0"] 35 | }, 36 | { 37 | "regex": "^[a-zA-Z]{2,}\\s[a-zA-Z]{1,}'?-?[a-zA-Z]{2,}\\s?([a-zA-Z]{1,})?$", 38 | "pass": ["John Doe", "Mary Jane", "Robert O'Neill", "Sarah Jane-Smith"], 39 | "fail": ["J D", "John", "John Doe", "12John Doe"] 40 | } 41 | ] 42 | -------------------------------------------------------------------------------- /packages/compiler/src/errors.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | #[derive(Error, Debug)] 4 | pub enum CompilerError { 5 | #[error("Failed to open file: {0}")] 6 | FileOpenError(#[from] std::io::Error), 7 | #[error("Failed to parse JSON: {0}")] 8 | JsonParseError(#[from] serde_json::Error), 9 | #[error("{0}")] 10 | GenericError(String), 11 | #[error( 12 | "Failed to build DFA for regex: \"{regex}\", please check your regex. Error: {source}" 13 | )] 14 | BuildError { 15 | regex: String, 16 | #[source] 17 | source: regex_automata::dfa::dense::BuildError, 18 | }, 19 | #[error("Error in Regex: {0}")] 20 | RegexError(#[from] regex::Error), 21 | #[error("Parse Error: {0}")] 22 | ParseError(String), 23 | #[error("Graph Error: {0}")] 24 | GraphError(String), 25 | #[error("No accepted state found in DFA")] 26 | NoAcceptedState, 27 | #[error("Accept Nodes Error: {0}")] 28 | AcceptNodesError(String), 29 | } 30 | -------------------------------------------------------------------------------- /packages/compiler/src/halo2.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | errors::CompilerError, 3 | regex::{get_accepted_state, get_max_state}, 4 | structs::RegexAndDFA, 5 | }; 6 | use std::{ 7 | fs::File, 8 | io::{BufWriter, Write}, 9 | path::PathBuf, 10 | }; 11 | 12 | /// Converts a RegexAndDFA structure to a text representation of the DFA. 13 | /// 14 | /// # Arguments 15 | /// 16 | /// * `regex_and_dfa` - A reference to the RegexAndDFA structure. 17 | /// 18 | /// # Returns 19 | /// 20 | /// A String containing the text representation of the DFA. 21 | fn dfa_to_regex_def_text(regex_and_dfa: &RegexAndDFA) -> String { 22 | let accepted_state = get_accepted_state(®ex_and_dfa.dfa).unwrap(); 23 | let max_state = get_max_state(®ex_and_dfa.dfa); 24 | let mut text = format!("0\n{}\n{}\n", accepted_state, max_state); 25 | 26 | for (i, state) in regex_and_dfa.dfa.states.iter().enumerate() { 27 | for (next_state, chars) in state.transitions.iter() { 28 | for &char in chars { 29 | text += &format!("{} {} {}\n", i, next_state, char as u8); 30 | } 31 | } 32 | } 33 | text 34 | } 35 | 36 | /// Generates Halo2 tables from a RegexAndDFA structure. 37 | /// 38 | /// # Arguments 39 | /// 40 | /// * `regex_and_dfa` - A reference to the RegexAndDFA structure. 41 | /// * `allstr_file_path` - The path where the main DFA definition will be written. 42 | /// * `substr_file_paths` - A slice of paths where substring definitions will be written. 43 | /// * `gen_substrs` - A boolean indicating whether to generate substring files. 44 | /// 45 | /// # Returns 46 | /// 47 | /// A Result indicating success or containing a CompilerError. 48 | pub(crate) fn gen_halo2_tables( 49 | regex_and_dfa: &RegexAndDFA, 50 | allstr_file_path: &PathBuf, 51 | substr_file_paths: &[PathBuf], 52 | gen_substrs: bool, 53 | ) -> Result<(), CompilerError> { 54 | let regex_text = dfa_to_regex_def_text(regex_and_dfa); 55 | std::fs::write(allstr_file_path, regex_text)?; 56 | 57 | if !gen_substrs { 58 | return Ok(()); 59 | } 60 | 61 | for (idx, defs) in regex_and_dfa.substrings.substring_ranges.iter().enumerate() { 62 | let mut writer = BufWriter::new(File::create(&substr_file_paths[idx])?); 63 | let (starts, ends) = ®ex_and_dfa 64 | .substrings 65 | .substring_boundaries 66 | .as_ref() 67 | .unwrap()[idx]; 68 | 69 | writeln!( 70 | writer, 71 | "{}", 72 | starts 73 | .iter() 74 | .map(ToString::to_string) 75 | .collect::>() 76 | .join(" ") 77 | )?; 78 | writeln!( 79 | writer, 80 | "{}", 81 | ends.iter() 82 | .map(ToString::to_string) 83 | .collect::>() 84 | .join(" ") 85 | )?; 86 | 87 | let mut sorted_defs: Vec<_> = defs.iter().collect(); 88 | sorted_defs.sort_unstable_by_key(|&(start, end)| (*start, *end)); 89 | 90 | for &(cur, next) in &sorted_defs { 91 | writeln!(writer, "{} {}", cur, next)?; 92 | } 93 | } 94 | 95 | Ok(()) 96 | } 97 | -------------------------------------------------------------------------------- /packages/compiler/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod circom; 2 | mod errors; 3 | mod halo2; 4 | mod regex; 5 | mod structs; 6 | mod wasm; 7 | 8 | use circom::gen_circom_template; 9 | use errors::CompilerError; 10 | use halo2::gen_halo2_tables; 11 | use itertools::Itertools; 12 | use regex::{create_regex_and_dfa_from_str_and_defs, get_regex_and_dfa}; 13 | use std::{fs::File, path::PathBuf}; 14 | use structs::{RegexAndDFA, SubstringDefinitionsJson}; 15 | 16 | pub use structs::{DecomposedRegexConfig, RegexPartConfig}; 17 | 18 | /// Loads substring definitions from a JSON file or creates a default one. 19 | /// 20 | /// # Arguments 21 | /// 22 | /// * `substrs_json_path` - An optional path to the JSON file containing substring definitions. 23 | /// 24 | /// # Returns 25 | /// 26 | /// A `Result` containing either the loaded `SubstringDefinitionsJson` or a `CompilerError`. 27 | fn load_substring_definitions_json( 28 | substrs_json_path: Option<&str>, 29 | ) -> Result { 30 | match substrs_json_path { 31 | Some(path) => { 32 | let file = File::open(path)?; 33 | serde_json::from_reader(file).map_err(CompilerError::JsonParseError) 34 | } 35 | None => Ok(SubstringDefinitionsJson { 36 | transitions: vec![vec![]], 37 | }), 38 | } 39 | } 40 | 41 | /// Generates output files for Halo2 and Circom based on the provided regex and DFA. 42 | /// 43 | /// # Arguments 44 | /// 45 | /// * `regex_and_dfa` - The `RegexAndDFA` struct containing the regex pattern and DFA. 46 | /// * `halo2_dir_path` - An optional path to the directory for Halo2 output files. 47 | /// * `circom_file_path` - An optional path to the Circom output file. 48 | /// * `circom_template_name` - An optional name for the Circom template. 49 | /// * `num_public_parts` - The number of public parts in the regex. 50 | /// * `gen_substrs` - A boolean indicating whether to generate substrings. 51 | /// 52 | /// # Returns 53 | /// 54 | /// A `Result` indicating success or a `CompilerError`. 55 | fn generate_outputs( 56 | regex_and_dfa: &RegexAndDFA, 57 | halo2_dir_path: Option<&str>, 58 | circom_file_path: Option<&str>, 59 | circom_template_name: Option<&str>, 60 | num_public_parts: usize, 61 | gen_substrs: bool, 62 | ) -> Result<(), CompilerError> { 63 | if let Some(halo2_dir_path) = halo2_dir_path { 64 | let halo2_dir_path = PathBuf::from(halo2_dir_path); 65 | let allstr_file_path = halo2_dir_path.join("allstr.txt"); 66 | let substr_file_paths = (0..num_public_parts) 67 | .map(|idx| halo2_dir_path.join(format!("substr_{}.txt", idx))) 68 | .collect_vec(); 69 | 70 | gen_halo2_tables( 71 | regex_and_dfa, 72 | &allstr_file_path, 73 | &substr_file_paths, 74 | gen_substrs, 75 | )?; 76 | } 77 | 78 | if let Some(circom_file_path) = circom_file_path { 79 | let circom_file_path = PathBuf::from(circom_file_path); 80 | let circom_template_name = circom_template_name 81 | .expect("circom template name must be specified if circom file path is specified"); 82 | 83 | gen_circom_template( 84 | regex_and_dfa, 85 | &circom_file_path, 86 | &circom_template_name, 87 | gen_substrs, 88 | )?; 89 | } 90 | 91 | Ok(()) 92 | } 93 | 94 | /// Generates outputs from a decomposed regex configuration file. 95 | /// 96 | /// # Arguments 97 | /// 98 | /// * `decomposed_regex_path` - The path to the decomposed regex configuration file. 99 | /// * `halo2_dir_path` - An optional path to the directory for Halo2 output files. 100 | /// * `circom_file_path` - An optional path to the Circom output file. 101 | /// * `circom_template_name` - An optional name for the Circom template. 102 | /// * `gen_substrs` - An optional boolean indicating whether to generate substrings. 103 | /// 104 | /// # Returns 105 | /// 106 | /// A `Result` indicating success or a `CompilerError`. 107 | pub fn gen_from_decomposed( 108 | decomposed_regex_path: &str, 109 | halo2_dir_path: Option<&str>, 110 | circom_file_path: Option<&str>, 111 | circom_template_name: Option<&str>, 112 | gen_substrs: Option, 113 | ) -> Result<(), CompilerError> { 114 | let mut decomposed_regex_config: DecomposedRegexConfig = 115 | serde_json::from_reader(File::open(decomposed_regex_path)?)?; 116 | let gen_substrs = gen_substrs.unwrap_or(false); 117 | 118 | let regex_and_dfa = get_regex_and_dfa(&mut decomposed_regex_config)?; 119 | 120 | let num_public_parts = decomposed_regex_config 121 | .parts 122 | .iter() 123 | .filter(|part| part.is_public) 124 | .count(); 125 | 126 | generate_outputs( 127 | ®ex_and_dfa, 128 | halo2_dir_path, 129 | circom_file_path, 130 | circom_template_name, 131 | num_public_parts, 132 | gen_substrs, 133 | )?; 134 | 135 | Ok(()) 136 | } 137 | 138 | /// Generates outputs from a raw regex string and optional substring definitions. 139 | /// 140 | /// # Arguments 141 | /// 142 | /// * `raw_regex` - The raw regex string. 143 | /// * `substrs_json_path` - An optional path to the JSON file containing substring definitions. 144 | /// * `halo2_dir_path` - An optional path to the directory for Halo2 output files. 145 | /// * `circom_file_path` - An optional path to the Circom output file. 146 | /// * `template_name` - An optional name for the Circom template. 147 | /// * `gen_substrs` - An optional boolean indicating whether to generate substrings. 148 | /// 149 | /// # Returns 150 | /// 151 | /// A `Result` indicating success or a `CompilerError`. 152 | pub fn gen_from_raw( 153 | raw_regex: &str, 154 | substrs_json_path: Option<&str>, 155 | halo2_dir_path: Option<&str>, 156 | circom_file_path: Option<&str>, 157 | template_name: Option<&str>, 158 | gen_substrs: Option, 159 | ) -> Result<(), CompilerError> { 160 | let substrs_defs_json = load_substring_definitions_json(substrs_json_path)?; 161 | let num_public_parts = substrs_defs_json.transitions.len(); 162 | 163 | let regex_and_dfa = create_regex_and_dfa_from_str_and_defs(raw_regex, substrs_defs_json)?; 164 | 165 | let gen_substrs = gen_substrs.unwrap_or(true); 166 | 167 | generate_outputs( 168 | ®ex_and_dfa, 169 | halo2_dir_path, 170 | circom_file_path, 171 | template_name, 172 | num_public_parts, 173 | gen_substrs, 174 | )?; 175 | 176 | Ok(()) 177 | } 178 | 179 | /// Generates Circom output from a decomposed regex configuration. 180 | /// 181 | /// # Arguments 182 | /// 183 | /// * `decomposed_regex` - A mutable reference to the `DecomposedRegexConfig` containing the regex parts. 184 | /// * `circom_file_path` - An optional path to the Circom output file. 185 | /// * `circom_template_name` - An optional name for the Circom template. 186 | /// * `gen_substrs` - An optional boolean indicating whether to generate substrings. 187 | /// 188 | /// # Returns 189 | /// 190 | /// A `Result` indicating success or a `CompilerError`. 191 | pub fn gen_circom_from_decomposed_regex( 192 | decomposed_regex: &mut DecomposedRegexConfig, 193 | circom_file_path: Option<&str>, 194 | circom_template_name: Option<&str>, 195 | gen_substrs: Option, 196 | ) -> Result<(), CompilerError> { 197 | let gen_substrs = gen_substrs.unwrap_or(false); 198 | 199 | let regex_and_dfa = get_regex_and_dfa(decomposed_regex)?; 200 | 201 | let num_public_parts = decomposed_regex 202 | .parts 203 | .iter() 204 | .filter(|part| part.is_public) 205 | .count(); 206 | 207 | generate_outputs( 208 | ®ex_and_dfa, 209 | None, 210 | circom_file_path, 211 | circom_template_name, 212 | num_public_parts, 213 | gen_substrs, 214 | )?; 215 | 216 | Ok(()) 217 | } 218 | 219 | #[cfg(target_arch = "wasm32")] 220 | pub use crate::wasm::*; 221 | -------------------------------------------------------------------------------- /packages/compiler/src/structs.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use std::collections::{BTreeMap, BTreeSet, VecDeque}; 3 | 4 | #[derive(Debug, Clone, Serialize, Deserialize)] 5 | pub struct RegexPartConfig { 6 | pub is_public: bool, 7 | pub regex_def: String, 8 | } 9 | 10 | #[derive(Debug, Clone, Serialize, Deserialize)] 11 | pub struct DecomposedRegexConfig { 12 | pub parts: VecDeque, 13 | } 14 | 15 | #[derive(Debug, Clone)] 16 | pub struct DFAStateInfo { 17 | pub typ: String, 18 | pub source: usize, 19 | pub edges: BTreeMap, 20 | } 21 | 22 | #[derive(Debug)] 23 | pub struct DFAGraphInfo { 24 | pub states: Vec, 25 | } 26 | 27 | #[derive(Debug, Clone, Serialize, Deserialize)] 28 | pub struct DFAStateNode { 29 | pub state_type: String, 30 | pub state_id: usize, 31 | pub transitions: BTreeMap>, 32 | } 33 | 34 | #[derive(Debug, Clone, Serialize, Deserialize)] 35 | pub struct DFAGraph { 36 | pub states: Vec, 37 | } 38 | 39 | #[derive(Debug, Clone, Serialize, Deserialize)] 40 | pub struct SubstringDefinitions { 41 | pub substring_ranges: Vec>, 42 | pub substring_boundaries: Option, BTreeSet)>>, 43 | } 44 | 45 | #[derive(Debug, Clone, Serialize, Deserialize)] 46 | pub struct RegexAndDFA { 47 | pub regex_pattern: String, 48 | pub dfa: DFAGraph, 49 | pub has_end_anchor: bool, 50 | pub substrings: SubstringDefinitions, 51 | } 52 | 53 | #[derive(Debug, Clone, Serialize, Deserialize)] 54 | pub struct SubstringDefinitionsJson { 55 | pub transitions: Vec>, 56 | } 57 | -------------------------------------------------------------------------------- /packages/compiler/src/wasm.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use console_error_panic_hook; 3 | use serde_wasm_bindgen::from_value; 4 | use std::panic; 5 | use wasm_bindgen::prelude::*; 6 | 7 | use self::circom::gen_circom_string; 8 | 9 | #[wasm_bindgen] 10 | #[allow(non_snake_case)] 11 | pub fn genFromDecomposed( 12 | decomposedRegexJson: &str, 13 | circomTemplateName: &str, 14 | ) -> Result { 15 | let mut decomposed_regex_config: DecomposedRegexConfig = 16 | serde_json::from_str(decomposedRegexJson).map_err(|e| { 17 | JsValue::from_str(&format!("failed to parse decomposed_regex json: {}", e)) 18 | })?; 19 | 20 | let regex_and_dfa = get_regex_and_dfa(&mut decomposed_regex_config).map_err(|e| { 21 | JsValue::from_str(&format!( 22 | "failed to convert the decomposed regex to dfa: {}", 23 | e 24 | )) 25 | })?; 26 | 27 | gen_circom_string(®ex_and_dfa, circomTemplateName) 28 | .map_err(|e| JsValue::from_str(&format!("Failed to generate Circom string: {}", e))) 29 | } 30 | 31 | #[wasm_bindgen] 32 | #[allow(non_snake_case)] 33 | pub fn genFromRaw(rawRegex: &str, substrsJson: &str, circomTemplateName: &str) -> String { 34 | let substrs_defs_json: SubstringDefinitionsJson = 35 | serde_json::from_str(substrsJson).expect("failed to parse substrs json"); 36 | let regex_and_dfa = create_regex_and_dfa_from_str_and_defs(rawRegex, substrs_defs_json) 37 | .expect("failed to convert the raw regex and state transitions to dfa"); 38 | gen_circom_string(®ex_and_dfa, circomTemplateName).expect("failed to generate circom") 39 | } 40 | 41 | #[wasm_bindgen] 42 | #[allow(non_snake_case)] 43 | pub fn genRegexAndDfa(decomposedRegex: JsValue) -> JsValue { 44 | let mut decomposed_regex_config: DecomposedRegexConfig = 45 | from_value(decomposedRegex).expect("failed to parse decomposed regex"); 46 | let regex_and_dfa = get_regex_and_dfa(&mut decomposed_regex_config) 47 | .expect("failed to convert the decomposed regex to dfa"); 48 | let dfa_val_str = 49 | serde_json::to_string(®ex_and_dfa).expect("failed to convert the dfa to json"); 50 | JsValue::from_str(&dfa_val_str) 51 | } 52 | 53 | #[wasm_bindgen] 54 | #[allow(non_snake_case)] 55 | pub fn genCircom(decomposedRegex: JsValue, circomTemplateName: &str) -> String { 56 | let mut decomposed_regex_config: DecomposedRegexConfig = 57 | from_value(decomposedRegex).expect("failed to parse decomposed regex"); 58 | let regex_and_dfa = get_regex_and_dfa(&mut decomposed_regex_config) 59 | .expect("failed to convert the decomposed regex to dfa"); 60 | gen_circom_string(®ex_and_dfa, circomTemplateName).expect("failed to generate circom") 61 | } 62 | --------------------------------------------------------------------------------