diff --git a/packages/circuits/README.md b/packages/circuits/README.md index f76f3ac0..0574db0b 100644 --- a/packages/circuits/README.md +++ b/packages/circuits/README.md @@ -1,13 +1,17 @@ # Circuit Architecture + ## Circom Circuits + ### Main Circuits We provide one main circuit as follows. #### `email_auth.circom` + A circuit to verify that a message in the subject is authorized by a user of an account salt, derived from an email address in the From field and a random field value called account code. It takes as input the following data: + 1. a padded email header `padded_header`. 2. the bytes of the padded email header `padded_header_len`. 3. an RSA public key `public_key`. @@ -16,36 +20,43 @@ It takes as input the following data: 6. a starting position of the From field in the email header `from_addr_idx`. 7. a starting position of the Subject field in the email header `subject_idx`. 8. a starting position of the email domain in the email address of the From field `domain_idx`. -10. a starting position of the timestamp in the email header `timestamp_idx`. -11. a starting position of the invitation code in the email header `code_idx`. +9. a starting position of the timestamp in the email header `timestamp_idx`. +10. a starting position of the invitation code in the email header `code_idx`. Its instances are as follows: + 1. an email domain `domain_name`. 2. a Poseidon hash of the RSA public key `public_key_hash`. 3. a nullifier of the email `email_nullifier`. 4. a timestamp in the email header `timestamp`. -5. a masked subject where characters either in the email address or in the invitation code are replaced with zero `masked_subject_str`. +5. a masked subject where characters either in the email address or in the invitation code are replaced with zero `masked_subject_str`. 6. an account salt `account_salt`. 7. a flag whether the email header contains the invitation code `is_code_exist`. ## How to Use + ### Build circuits + `yarn && yarn build` ### Run tests + At `packages/circuits`, make a `build` directory, download the zip file from the following link, and place its unzipped directory under `build`. https://drive.google.com/file/d/1TChinAnHr9eV8H_OV9SVReF8Rvu6h1XH/view?usp=sharing -Then, move `email_auth.zkey` in the unzipped directory `params` to `build`. +Then, move `email_auth.zkey` in the unzipped directory `params` to `build`. Then run the following command. `yarn test` ### Generate proving keys and verifier contracts for main circuits + `yarn dev-setup` ## Specification + The `email_auth.circom` makes constraints and computes the public output as follows. + 1. Assert that `signature` is valid for `padded_header` and `public_key`. 2. Let `public_key_hash` be `PoseidonHash(public_key)`. 3. Let `email_nullifier` be `PoseidonHash(PoseidonHash(signature))`. @@ -57,17 +68,57 @@ The `email_auth.circom` makes constraints and computes the public output as foll 9. If `is_time_exist` is 1, let `timestamp` be an integer parsing `timestamp_str` as a digit string. Otherwise, let `timestamp` be zero. 10. Let `is_code_exist` be 1 if `padded_header` satisfies the regex of the invitation code. 11. Let `code_str` be `padded_header[code_idx:code_idx+64]`. -12. Let `embedded_code` be an integer parsing `code_str` as a hex string. +12. Let `embedded_code` be an integer parsing `code_str` as a hex string. 13. If `is_code_exist` is 1, assert that `embedded_code` is equal to `account_code`. 14. Let `account_salt` be `PoseidonHash(from_addr|0..0, account_code, 0)`. 15. Let `masked_subject` be a string that removes the invitation code with the prefix `code_str` and one email address from `subject`, if they appear in `subject`. Note that the email address in the subject is assumbed not to overlap with the invitation code. - #### `email_auth_with_body_parsing_with_qp_encoding.circom` + A circuit to verify that a message in the email body, called command, is authorized by a user of an account salt, derived from an email address in the From field and a random field value called account code. This is basically the same as the `email_auth.circom` described above except for the following features: + - Instead of `subject_idx`, it additionally takes as a private input a padded email body `padded_cleaned_body` and an index of the command in the email body `command_idx`. - It extracts a substring `command` between a prefix `(
]*>)"` and a suffix `
` from `padded_cleaned_body`. -- It outputs `masked_command` instead of `masked_subject`, which removes the invitation code with the prefix and one email address from `command`. \ No newline at end of file +- It outputs `masked_command` instead of `masked_subject`, which removes the invitation code with the prefix and one email address from `command`. + +# Circuit Scripts + +## Usage + +### Build a Circuit + +```bash +npm run build [output_dir] +``` + +Examples: + +```bash +npm run build -- email_auth # builds email_auth circuit to build/ +npm run build -- email_auth_reveal # builds email_auth_reveal circuit to build/ +npm run build -- email_auth_reveal reveal # builds email_auth_reveal circuit to build/reveal/ +``` + +### Dev Setup a Circuit + +```bash +npm run dev-setup -- [output_dir] +``` + +Examples: + +```bash +npm run dev-setup email_auth # dev setup for email_auth circuit to build/ +npm run dev-setup email_auth_reveal # dev setup for email_auth_reveal circuit to build/ +npm run dev-setup email_auth_reveal reveal # dev setup for email_auth_reveal circuit to build/reveal/ +``` + +## Notes + +- Circuit files should be placed in the `src/` directory with `.circom` extension +- Circuit name is required - you must specify which circuit to build/setup +- Default output directory is `.` (directly in build/) if not specified +- Output directories are created under `build/` folder diff --git a/packages/circuits/package.json b/packages/circuits/package.json index 5be6fd96..b1a55410 100644 --- a/packages/circuits/package.json +++ b/packages/circuits/package.json @@ -3,12 +3,8 @@ "license": "MIT", "version": "1.0.3", "scripts": { - "build": "mkdir -p build && circom src/email_auth.circom --r1cs --wasm --sym --c -l ../../node_modules -o ./build", - "build-no-timestamp": "mkdir -p build/no-timestamp && circom src/email_auth_no_timestamp.circom --r1cs --wasm --sym --c -l ../../node_modules -o ./build/no-timestamp", - "build-legacy": "mkdir -p build && circom src/email_auth_legacy.circom --r1cs --wasm --sym --c -l ../../node_modules -o ./build", - "build-recipient": "mkdir -p build && circom src/email_auth_with_recipient.circom --r1cs --wasm --sym --c -l ../../node_modules -o ./build", - "dev-setup": "NODE_OPTIONS=--max_old_space_size=16384 npx ts-node scripts/dev-setup.ts --output ./build --circuit email_auth", - "dev-setup-no-timestamp": "NODE_OPTIONS=--max_old_space_size=16384 npx ts-node scripts/dev-setup.ts --output ./build/no-timestamp --circuit email_auth_no_timestamp", + "build": "NODE_OPTIONS=--max_old_space_size=16384 node -e \"const args=process.argv.slice(2); const circuit=args[0]; const output=args[1]||'.'; if(!circuit) throw new Error('Circuit name required: npm run build [output_dir]'); require('child_process').execSync('mkdir -p build/'+output+' && circom src/'+circuit+'.circom --r1cs --wasm --sym --c -l ../../node_modules -o ./build/'+output, {stdio:'inherit'})\"", + "dev-setup": "NODE_OPTIONS=--max_old_space_size=16384 node -e \"const args=process.argv.slice(2); const circuit=args[0]; const output=args[1]||'.'; if(!circuit) throw new Error('Circuit name required: npm run dev-setup [output_dir]'); require('child_process').execSync('NODE_OPTIONS=--max_old_space_size=16384 npx ts-node scripts/dev-setup.ts --output ./build/'+output+' --circuit '+circuit, {stdio:'inherit'})\"", "gen-input": "NODE_OPTIONS=--max_old_space_size=8192 npx ts-node scripts/gen_input.ts", "test": "NODE_OPTIONS=--max_old_space_size=16384 jest", "test-large": "NODE_OPTIONS=--max_old_space_size=32768 jest --maxWorkers=1" @@ -53,4 +49,4 @@ "package.json", "README.md" ] -} \ No newline at end of file +}