Merge branch 'refactor_upstream' into fe-refactor

This commit is contained in:
Saleel
2023-06-08 23:37:49 +05:30
50 changed files with 7973 additions and 1621 deletions

View File

@@ -8,9 +8,13 @@ jobs:
- image: ghcr.io/foundry-rs/foundry:latest - image: ghcr.io/foundry-rs/foundry:latest
steps: steps:
- checkout - checkout
- run:
name: install foundry packages
command: forge install openzeppelin/openzeppelin-contracts foundry-rs/forge-std openzeppelin/openzeppelin-contracts-upgradeable dapphub/ds-test --no-git
working_directory: src/contracts
- run: - run:
name: run foundry tests name: run foundry tests
command: forge test --via-ir command: forge test --fork-url https://eth-goerli.g.alchemy.com/v2/${ALCHEMY_API_KEY}
working_directory: src/contracts working_directory: src/contracts
run_unit_and_e2e_tests: run_unit_and_e2e_tests:
@@ -47,7 +51,7 @@ jobs:
- store_test_results: - store_test_results:
path: reports/ path: reports/
workflows: workflows:
build_test: build_test:
jobs: jobs:
- run_unit_and_e2e_tests - run_unit_and_e2e_tests
- run_forge_tests - run_forge_tests

8
.gitignore vendored
View File

@@ -6,7 +6,8 @@ dist
# dependencies # dependencies
/node_modules /node_modules
**/node_modules /e2e-lambdatest/node_modules/
/e2e-lambdatest/.yarn/
/.pnp /.pnp
.pnp.js .pnp.js
@@ -53,10 +54,13 @@ circuits/email/
cache/ cache/
test.log test.log
src/contracts/out/ src/contracts/out/
src/contracts/lib/
src/contracts/broadcast
.next .next
node_modules.nosync
*Wallet* *Wallet*
*wallet* *wallet*
node_modules.nosync circuits/regexes/subject_regex*
# Files that never should be committed, but can be obtained by asking Aayush or generating them yourself # Files that never should be committed, but can be obtained by asking Aayush or generating them yourself
*.eml *.eml

8
.gitmodules vendored
View File

@@ -1,6 +1,14 @@
[submodule "src/contracts/lib/openzeppelin-contracts"] [submodule "src/contracts/lib/openzeppelin-contracts"]
path = src/contracts/lib/openzeppelin-contracts path = src/contracts/lib/openzeppelin-contracts
url = https://github.com/openzeppelin/openzeppelin-contracts url = https://github.com/openzeppelin/openzeppelin-contracts
branch = v4.8.1
[submodule "src/contracts/lib/forge-std"] [submodule "src/contracts/lib/forge-std"]
path = src/contracts/lib/forge-std path = src/contracts/lib/forge-std
url = https://github.com/foundry-rs/forge-std url = https://github.com/foundry-rs/forge-std
[submodule "src/contracts/lib/openzeppelin-contracts-upgradeable"]
path = src/contracts/lib/openzeppelin-contracts-upgradeable
url = https://github.com/openzeppelin/openzeppelin-contracts-upgradeable
branch = v4.9.0
[submodule "src/contracts/lib/ds-test"]
path = src/contracts/lib/ds-test
url = https://github.com/dapphub/ds-test

101
README.md
View File

@@ -14,13 +14,13 @@ The documentation for the app is located at https://zkemail.xyz/docs (WIP). Made
To run the frontend with existing circuits (there is no backend or server), enable Node 16 (with nvm) and run: To run the frontend with existing circuits (there is no backend or server), enable Node 16 (with nvm) and run:
``` ```bash
yarn start yarn start
``` ```
If the frontend shows an error on fullProve line, run this and rerun If the frontend shows an error on fullProve line, run this and rerun
``` ```bash
yarn add snarkjs@https://github.com/sampritipanda/snarkjs.git#fef81fc51d17a734637555c6edbd585ecda02d9e yarn add snarkjs@https://github.com/sampritipanda/snarkjs.git#fef81fc51d17a734637555c6edbd585ecda02d9e
``` ```
@@ -44,6 +44,8 @@ circuits/ # groth16 zk circuits
input_email_domain.json # Standard input for from/to mit.edu domain matching, for use with circuit without body checks input_email_domain.json # Standard input for from/to mit.edu domain matching, for use with circuit without body checks
input_email_packed.json # Same as above but has useless packed input -- is private so irrelevant, this file could be deleted. input_email_packed.json # Same as above but has useless packed input -- is private so irrelevant, this file could be deleted.
main/ # Legacy RSA code main/ # Legacy RSA code
regexes/ # Generated regexes
helpers/ # Common helper circom circuits imported in email circuits
scripts/ # Run snarkjs ceremony to generate zkey with yarn compile scripts/ # Run snarkjs ceremony to generate zkey with yarn compile
dizkus-scripts/ dizkus-scripts/
*.sh # Scripts to compile the chunked keys on a remote server *.sh # Scripts to compile the chunked keys on a remote server
@@ -72,12 +74,12 @@ public/ # Should contain vkey/wasm, but we end up fetching those from AWS server
### Regex to Circom ### Regex to Circom
First, generate a regex. Go to our [min_dfa fork](https://mindfa.onrender.com/min_dfa) of cyberzhg's toolbox and insert your regex on the top line. We've forked [min-dfa into a UI here](https://mindfa.onrender.com/min_dfa) to create a UI that converts existing regexes with [] support, as well as escapes \_, and the character classes a-z, A-Z, and 0-9. It also shows the DFA states very clearly so you can choose accept states easily. This should make converting regexes into DFA form way cleaner. See regex_to_circom/README.md for usage instructions.
Modify either `let raw_regex = ` (that supports actual regex strings like `[A-Za-z0-9]` [but no other character ranges]) or modify `let regex = ` (that does not support brackets or character ranges and supports only the limited syntax in https://cyberzhg.github.io/toolbox/min_dfa) in regex_to_circom/regex_to_dfa.js and then run `python3 gen.py`.
### Email Circuit Build Steps ### Email Circuit Build Steps
#### Build
Install rust/circom2 via the following steps, according to: https://docs.circom.io/getting-started/installation/ Install rust/circom2 via the following steps, according to: https://docs.circom.io/getting-started/installation/
```bash ```bash
@@ -96,7 +98,7 @@ cargo install --path circom
Inside `zk-email-verify` folder, do Inside `zk-email-verify` folder, do
``` ```bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash # If don't have npm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash # If don't have npm
. ~/.nvm/nvm.sh # If don't have npm . ~/.nvm/nvm.sh # If don't have npm
nvm install 16 # If don't have node 16 nvm install 16 # If don't have node 16
@@ -119,6 +121,8 @@ mv powersOfTau28_hez_final_21.ptau circuits/powersOfTau28_hez_final_21.ptau
<!-- Previously snarkjs@git+https://github.com/vb7401/snarkjs.git#fae4fe381bdad2da13eee71010dfe477fc694ac1 --> <!-- Previously snarkjs@git+https://github.com/vb7401/snarkjs.git#fae4fe381bdad2da13eee71010dfe477fc694ac1 -->
<!-- Now -> yarn add https://github.com/vb7401/snarkjs/commits/chunk_zkey_gen --> <!-- Now -> yarn add https://github.com/vb7401/snarkjs/commits/chunk_zkey_gen -->
#### Zkey Creation
Put the email into ...\*.eml. Edit the constant filename at the top of generate_input.ts to import that file, then use the output of running that file as the input file (you may need to rename it). You'll need this for both zkey and verifier generation. Put the email into ...\*.eml. Edit the constant filename at the top of generate_input.ts to import that file, then use the output of running that file as the input file (you may need to rename it). You'll need this for both zkey and verifier generation.
To create a chunked zkey for in-browser proving, run the following on a high CPU computer: To create a chunked zkey for in-browser proving, run the following on a high CPU computer:
@@ -129,15 +133,17 @@ cd dizkus-scripts/
cp entropy.env.example entropy.env cp entropy.env.example entropy.env
``` ```
Not put random characters into the values for entropy1 and entropy2, and hexadecimal characters into the beacon. These scripts will compile and test your zkey for you. Fill out the env via random characters into the values for entropy1 and entropy2, and hexadecimal characters into the beacon. These scripts will compile and test your zkey for you, and generate a normal zkey with for an on chain verifier or server side prover, with the same entropy as the chunked one. If you only want the chunked one, use ./3_gen_chunk_zkey.sh in place of the generation.
```bash
./1_compile.sh && ./2_gen_wtns.sh && ./3_gen_both_zkeys.sh && ./4_gen_vkey.sh && ./5_gen_proof.sh
``` ```
./1_compile.sh && ./2_gen_wtns.sh && ./3_gen_chunk_zkey.sh && ./4_gen_vkey.sh && ./5_gen_proof.sh
``` #### Server-side Prover: Rapidsnark Setup (Optional)
If you want to run a fast server side prover, install rapidsnark and test proofgen: If you want to run a fast server side prover, install rapidsnark and test proofgen:
``` ```bash
cd ../../ cd ../../
git clone https://github.com/iden3/rapidsnark git clone https://github.com/iden3/rapidsnark
cd rapidsnark cd rapidsnark
@@ -149,7 +155,7 @@ npx task createFieldSources
You're supposed to run `npx task buildPistache` next, but that errored, so I had to manually build the pistache lib first: You're supposed to run `npx task buildPistache` next, but that errored, so I had to manually build the pistache lib first:
``` ```bash
cd depends/pistache cd depends/pistache
sudo apt-get install meson ninja-build sudo apt-get install meson ninja-build
meson setup build --buildtype=release meson setup build --buildtype=release
@@ -161,22 +167,28 @@ cd ../..
Then, from rapidsnark/ I could run Then, from rapidsnark/ I could run
``` ```bash
npx task buildProverServer npx task buildProverServer
``` ```
And from zk-email-verify, convert your proof params to a rapidsnark friendly version: And from zk-email-verify, convert your proof params to a rapidsnark friendly version, generating the C-based witness generator and rapidsnark prover. To target to the AWS autoprover, go to the Makefile and manually replace the `CFLAGS=-std=c++11 -O3 -I.` line with (targeted to g4dn.xlarge and g5.xlarge, tuned to g5.xlarge):
```
```bash
cd ../zk-email-verify/dizkus-scripts cd ../zk-email-verify/dizkus-scripts
./6_gen_proof_rapidsnark.sh ./6_gen_proof_rapidsnark.sh
``` ```
To compile a non-chunked zkey for server-side use only,
```bash
yarn compile-all
```
#### Uploading to AWS
To upload zkeys to an s3 box on AWS, change bucket_name in upload_to_s3.py and run: To upload zkeys to an s3 box on AWS, change bucket_name in upload_to_s3.py and run:
``` ```bash
sudo apt install awscli # Ubuntu sudo apt install awscli # Ubuntu
brew install awscli # Mac brew install awscli # Mac
@@ -188,30 +200,24 @@ yarn add snarkjs@https://github.com/sampritipanda/snarkjs.git#fef81fc51d17a73463
If you want to upload different files, you can parameterize the script as well: If you want to upload different files, you can parameterize the script as well:
``` ```bash
python3 dizkus-scripts/upload_to_s3.py --dirs ~/zk-email-verify/build/email/email_js/ --bucket_name zkemail-zkey-chunks --prefix email.wasm python3 dizkus-scripts/upload_to_s3.py --dirs ~/zk-email-verify/build/email/email_js/ --bucket_name zkemail-zkey-chunks --prefix email.wasm
``` ```
Note that there's no .zkeya file, only .zkeyb ... .zkeyk. The script will automatically zip into .tar.gz files and load into s3 bucket. Note that there's no .zkeya file, only .zkeyb ... .zkeyk. The script will automatically zip into .tar.gz files and load into s3 bucket.
We use a fork of [zkp.ts](https://github.com/personaelabs/heyanon/blob/main/lib/zkp.ts) to load these keys into localforage. In the browser, to read off of localforage, you have to use this fork when running the frontend locally/in prod: #### Recompile Frontend (Important!)
``` We use a fork of [zkp.ts](https://github.com/personaelabs/heyanon/blob/main/lib/zkp.ts) to load these keys into localforage. In the browser, to read off of localforage, you have to use this fork when running the frontend locally/in prod. THIS IS VERY IMPORTANT -- WRONG SNARKJS FORKS CAUSE THE MOST ERRORS.
```bash
yarn install snarkjs@git+https://github.com/vb7401/snarkjs.git#53e86631b5e409e5bd30300611b495ca469503bc yarn install snarkjs@git+https://github.com/vb7401/snarkjs.git#53e86631b5e409e5bd30300611b495ca469503bc
``` ```
Manually copy paste the modulus in the resulting generated file into solidity verified mailserver keys. Manually copy paste the modulus in the resulting generated file into solidity verified mailserver keys.
Change s3 address in the frontend to your bucket. Change s3 address in the frontend to your bucket.
To do a non-chunked zkey for non-browser running,
```
yarn compile-all
```
### Really Large Circuits ### Really Large Circuits
If your circuit ends up being > 20M constraints, you will need to follow [these guidelines](https://hackmd.io/V-7Aal05Tiy-ozmzTGBYPA?view#Compilation-and-proving) to compile it. If your circuit ends up being > 20M constraints, you will need to follow [these guidelines](https://hackmd.io/V-7Aal05Tiy-ozmzTGBYPA?view#Compilation-and-proving) to compile it.
@@ -243,16 +249,20 @@ and you can swap `email` for `sha` or `rsa` or any other circuit name that match
and when the circuit doesn't change, and when the circuit doesn't change,
``` ```bash
yarn compile email true skip-r1cswasm yarn compile email true skip-r1cswasm
``` ```
and when the zkey also doesn't change, and when the zkey also doesn't change,
``` ```bash
yarn compile email true skip-r1cswasm skip-zkey yarn compile email true skip-r1cswasm skip-zkey
``` ```
### Contract Deployment
Follow the instructions in `src/contracts/README.md`.
### Production ### Production
For production, make sure to set a beacon in .env. For production, make sure to set a beacon in .env.
@@ -263,39 +273,20 @@ Note that this leaks the number of characters in the username of someone who sen
To constraint count, do To constraint count, do
``` ```bash
cd circuits cd circuits
node --max-old-space-size=614400 ./../node_modules/.bin/snarkjs r1cs info email.r1cs node --max-old-space-size=614400 ./../node_modules/.bin/snarkjs r1cs info email.r1cs
``` ```
To test solidity, To test solidity,
``` ```bash
cp node_modules/forge-std src/contracts/lib/forge-std cp node_modules/forge-std src/contracts/lib/forge-std
cd src/contracts cd src/contracts
forge test forge test
``` ```
To deploy contract to forked mainnet, do: To deploy contracts, look at src/contracts/README.md.
```
anvil --fork-url https://eth-mainnet.alchemyapi.io/v2/***REMOVED*** --port 8547 # Run in tmux
export ETH_RPC_URL=http://localhost:8547
# Public anvil sk
export SK=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
forge create --rpc-url $ETH_RPC_URL StringUtils --private-key $SK --via-ir --force
forge create --rpc-url $ETH_RPC_URL NFTSVG --private-key $SK --via-ir --force
# Edit the Cargo.toml to have the two deployment addresses, then call this
forge create --rpc-url $ETH_RPC_URL VerifiedTwitterEmail --private-key $SK --via-ir --force
```
For just the contracts, can do
```
forge create --rpc-url $ETH_RPC_URL src/contracts/src/emailVerifier.sol:Verifier --private-key $SK
```
## Performance ## Performance
@@ -327,7 +318,7 @@ Short term ways to improve the performance would be to replace the regex checks
Looking more long term, we are actively using Halo2 and Nova to speed up the most expensive operations of regex and SHA. As hash functions and regex DFA traversal are repeated operations, they are a great fit for Nova's folding methods to compress repeated computation into a constant sized folded instance. But to actually use Nova to fold expensive operations outside of Halo2/Groth16, we need to verify the folded instance is valid inside the circuit for zero-knowledge and to link it to the rest of the computation. We also are attempting to use the lookup feature of Halo2 to precompute the entire table of possible regex state transitions, and just looking up that all of the transitions made are valid ones in the table instead of expensively checking each state! This idea is due to Sora Suegami, explained in more detail here: https://hackmd.io/@SoraSuegami/Hy9dWgT8i. Looking more long term, we are actively using Halo2 and Nova to speed up the most expensive operations of regex and SHA. As hash functions and regex DFA traversal are repeated operations, they are a great fit for Nova's folding methods to compress repeated computation into a constant sized folded instance. But to actually use Nova to fold expensive operations outside of Halo2/Groth16, we need to verify the folded instance is valid inside the circuit for zero-knowledge and to link it to the rest of the computation. We also are attempting to use the lookup feature of Halo2 to precompute the entire table of possible regex state transitions, and just looking up that all of the transitions made are valid ones in the table instead of expensively checking each state! This idea is due to Sora Suegami, explained in more detail here: https://hackmd.io/@SoraSuegami/Hy9dWgT8i.
The current set of remaining tasks and potential final states is documented in the following DAG, please reach out if any of the projects seem interesting! The current set `of remaining tasks and potential final states is documented in the following DAG, please reach out if any of the projects seem interesting!
![Optimization plan](public/zk_email_optim.jpg) ![Optimization plan](public/zk_email_optim.jpg)
@@ -343,11 +334,11 @@ The full email header and body check circuit, with 7-byte packing and final publ
In the browser, on a 2019 Intel Mac on Chrome, proving uses 7.3/8 cores. zk-gen takes 384 s, groth16 prove takes 375 s, and witness calculation takes 9 s. In the browser, on a 2019 Intel Mac on Chrome, proving uses 7.3/8 cores. zk-gen takes 384 s, groth16 prove takes 375 s, and witness calculation takes 9 s.
For baremetal, proof generation time on 16 CPUs took 97 seconds. Generating zkey 0 took 17 minutes. zkey 1 and zkey 2 each took 5 minutes. r1cs + wasm generation took 5 minutes. Witness generation took 16 seconds. cpp generation of witness gen file (from script 6) took 210 minutes -- we do not run this pathway anymore. For baremetal, proof generation time on 16 CPUs took 97 seconds. Generating zkey 0 took 17 minutes. zkey 1 and zkey 2 each took 5 minutes. r1cs + wasm generation took 5 minutes. Witness generation took 16 seconds. cpp generation of witness gen file (from script 6) took 210 minutes -- this is only useful for server side proving.
### Scrubbing Sensitive Files ### Scrubbing Sensitive Files
``` ```bash
brew install git-filter-repo brew install git-filter-repo
git filter-repo --replace-text <(echo "0x000000000000000000000000000000000000000000000000000000000abcdef") git filter-repo --replace-text <(echo "0x000000000000000000000000000000000000000000000000000000000abcdef")
git filter-repo --path mit_msg.eml --invert-paths git filter-repo --path mit_msg.eml --invert-paths
@@ -401,7 +392,7 @@ Use https://sha256algorithm.com/ as an explainer! It's a great visualization of
### I'm having trouble with regex or base64 understanding. How do I understand that better? ### I'm having trouble with regex or base64 understanding. How do I understand that better?
Use https://cyberzhg.github.io/toolbox/ to experiement with conversions to/from base64 and to/from DFAs and NFAs. Use https://cyberzhg.github.io/toolbox/ to experiment with conversions to/from base64 and to/from DFAs and NFAs.
### What are the differences between generating proofs (snarkjs.groth16.fullprove) on the client vs. on a server? ### What are the differences between generating proofs (snarkjs.groth16.fullprove) on the client vs. on a server?

View File

@@ -1,6 +0,0 @@
// pragma circom 2.1.5;
// signal output reveal[num_bytes];
// for (var i = 0; i < num_bytes; i++) {
// reveal[i] <== in[i] * (states[i+1][18] + states[i+1][14]);
// }

View File

@@ -2,6 +2,7 @@ pragma circom 2.1.5;
include "../../node_modules/circomlib/circuits/bitify.circom"; include "../../node_modules/circomlib/circuits/bitify.circom";
include "../../node_modules/circomlib/circuits/comparators.circom"; include "../../node_modules/circomlib/circuits/comparators.circom";
include "../../node_modules/circomlib/circuits/mimcsponge.circom";
include "./fp.circom"; include "./fp.circom";
// returns ceil(log2(a+1)) // returns ceil(log2(a+1))
@@ -175,3 +176,28 @@ template Bytes2Packed(n){
} }
} }
} }
// salt_is_message_id_from, custom_anon_from_hashed_salt = MakeAnonEmailSalt(max_email_from_len, max_message_id_len)(email_from, custom_message_id_from, shifted_message_id)
template MakeAnonEmailSalt(email_len, blinder_len) {
signal input email[email_len];
signal input custom_message_id[blinder_len]; // previous message id, used to source past account
signal input original_message_id[blinder_len]; // previous message id, used to source past account
signal intermediate_is_message_id_from[blinder_len + 1];
signal isEq[blinder_len];
signal output blinder_matches;
signal output anon_salt;
component hasher = MiMCSponge(email_len + blinder_len, 220, 1);
hasher.k <== 123;
for (var i = 0; i < email_len; i++) {
hasher.ins[i] <== email[i];
}
intermediate_is_message_id_from[0] <== 1;
for (var i = 0; i < blinder_len; i++) {
hasher.ins[i + email_len] <== custom_message_id[i];
isEq[i] <== IsEqual()([custom_message_id[i], original_message_id[i]]);
intermediate_is_message_id_from[i + 1] <== isEq[i] * intermediate_is_message_id_from[i];
}
blinder_matches <== intermediate_is_message_id_from[blinder_len];
anon_salt <== hasher.outs[0];
}

View File

@@ -19,7 +19,7 @@ template FromRegex (msg_bytes) {
component multi_or[10][num_bytes]; component multi_or[10][num_bytes];
signal states[num_bytes+1][16]; signal states[num_bytes+1][16];
for (var i = 0; i < num_bytes; i++) { for (var i = 0; i < num_bytes+1; i++) {
states[i][0] <== 1; states[i][0] <== 1;
} }
for (var i = 1; i < 16; i++) { for (var i = 1; i < 16; i++) {
@@ -164,7 +164,7 @@ template FromRegex (msg_bytes) {
states[i+1][2] <== and[8][i].out; states[i+1][2] <== and[8][i].out;
eq[15][i] = IsEqual(); eq[15][i] = IsEqual();
eq[15][i].in[0] <== in[i]; eq[15][i].in[0] <== in[i];
eq[15][i].in[1] <== 94; eq[15][i].in[1] <== 128;
and[9][i] = AND(); and[9][i] = AND();
and[9][i].a <== states[i][0]; and[9][i].a <== states[i][0];
and[9][i].b <== eq[15][i].out; and[9][i].b <== eq[15][i].out;

View File

@@ -0,0 +1,291 @@
pragma circom 2.1.5;
include "./regex_helpers.circom";
template MessageIDRegex (msg_bytes) {
signal input msg[msg_bytes];
signal output out;
var num_bytes = msg_bytes+1;
signal in[num_bytes];
in[0] <== 128; // \x80 (sentinel for first character in string)
for (var i = 0; i < msg_bytes; i++) {
in[i+1] <== msg[i];
}
component eq[30][num_bytes];
component lt[12][num_bytes];
component and[26][num_bytes];
component multi_or[4][num_bytes];
signal states[num_bytes+1][19];
for (var i = 0; i < num_bytes+1; i++) {
states[i][0] <== 1;
}
for (var i = 1; i < 19; i++) {
states[0][i] <== 0;
}
for (var i = 0; i < num_bytes; i++) {
lt[0][i] = LessThan(8);
lt[0][i].in[0] <== 64;
lt[0][i].in[1] <== in[i];
lt[1][i] = LessThan(8);
lt[1][i].in[0] <== in[i];
lt[1][i].in[1] <== 91;
and[0][i] = AND();
and[0][i].a <== lt[0][i].out;
and[0][i].b <== lt[1][i].out;
lt[2][i] = LessThan(8);
lt[2][i].in[0] <== 96;
lt[2][i].in[1] <== in[i];
lt[3][i] = LessThan(8);
lt[3][i].in[0] <== in[i];
lt[3][i].in[1] <== 123;
and[1][i] = AND();
and[1][i].a <== lt[2][i].out;
and[1][i].b <== lt[3][i].out;
lt[4][i] = LessThan(8);
lt[4][i].in[0] <== 47;
lt[4][i].in[1] <== in[i];
lt[5][i] = LessThan(8);
lt[5][i].in[0] <== in[i];
lt[5][i].in[1] <== 58;
and[2][i] = AND();
and[2][i].a <== lt[4][i].out;
and[2][i].b <== lt[5][i].out;
eq[0][i] = IsEqual();
eq[0][i].in[0] <== in[i];
eq[0][i].in[1] <== 95;
eq[1][i] = IsEqual();
eq[1][i].in[0] <== in[i];
eq[1][i].in[1] <== 46;
eq[2][i] = IsEqual();
eq[2][i].in[0] <== in[i];
eq[2][i].in[1] <== 43;
eq[3][i] = IsEqual();
eq[3][i].in[0] <== in[i];
eq[3][i].in[1] <== 61;
eq[4][i] = IsEqual();
eq[4][i].in[0] <== in[i];
eq[4][i].in[1] <== 64;
eq[5][i] = IsEqual();
eq[5][i].in[0] <== in[i];
eq[5][i].in[1] <== 45;
and[3][i] = AND();
and[3][i].a <== states[i][1];
multi_or[0][i] = MultiOR(9);
multi_or[0][i].in[0] <== and[0][i].out;
multi_or[0][i].in[1] <== and[1][i].out;
multi_or[0][i].in[2] <== and[2][i].out;
multi_or[0][i].in[3] <== eq[0][i].out;
multi_or[0][i].in[4] <== eq[1][i].out;
multi_or[0][i].in[5] <== eq[2][i].out;
multi_or[0][i].in[6] <== eq[3][i].out;
multi_or[0][i].in[7] <== eq[4][i].out;
multi_or[0][i].in[8] <== eq[5][i].out;
and[3][i].b <== multi_or[0][i].out;
lt[6][i] = LessThan(8);
lt[6][i].in[0] <== 64;
lt[6][i].in[1] <== in[i];
lt[7][i] = LessThan(8);
lt[7][i].in[0] <== in[i];
lt[7][i].in[1] <== 91;
and[4][i] = AND();
and[4][i].a <== lt[6][i].out;
and[4][i].b <== lt[7][i].out;
lt[8][i] = LessThan(8);
lt[8][i].in[0] <== 96;
lt[8][i].in[1] <== in[i];
lt[9][i] = LessThan(8);
lt[9][i].in[0] <== in[i];
lt[9][i].in[1] <== 123;
and[5][i] = AND();
and[5][i].a <== lt[8][i].out;
and[5][i].b <== lt[9][i].out;
lt[10][i] = LessThan(8);
lt[10][i].in[0] <== 47;
lt[10][i].in[1] <== in[i];
lt[11][i] = LessThan(8);
lt[11][i].in[0] <== in[i];
lt[11][i].in[1] <== 58;
and[6][i] = AND();
and[6][i].a <== lt[10][i].out;
and[6][i].b <== lt[11][i].out;
eq[6][i] = IsEqual();
eq[6][i].in[0] <== in[i];
eq[6][i].in[1] <== 95;
eq[7][i] = IsEqual();
eq[7][i].in[0] <== in[i];
eq[7][i].in[1] <== 46;
eq[8][i] = IsEqual();
eq[8][i].in[0] <== in[i];
eq[8][i].in[1] <== 43;
eq[9][i] = IsEqual();
eq[9][i].in[0] <== in[i];
eq[9][i].in[1] <== 61;
eq[10][i] = IsEqual();
eq[10][i].in[0] <== in[i];
eq[10][i].in[1] <== 64;
eq[11][i] = IsEqual();
eq[11][i].in[0] <== in[i];
eq[11][i].in[1] <== 45;
and[7][i] = AND();
and[7][i].a <== states[i][18];
multi_or[1][i] = MultiOR(9);
multi_or[1][i].in[0] <== and[4][i].out;
multi_or[1][i].in[1] <== and[5][i].out;
multi_or[1][i].in[2] <== and[6][i].out;
multi_or[1][i].in[3] <== eq[6][i].out;
multi_or[1][i].in[4] <== eq[7][i].out;
multi_or[1][i].in[5] <== eq[8][i].out;
multi_or[1][i].in[6] <== eq[9][i].out;
multi_or[1][i].in[7] <== eq[10][i].out;
multi_or[1][i].in[8] <== eq[11][i].out;
and[7][i].b <== multi_or[1][i].out;
multi_or[2][i] = MultiOR(2);
multi_or[2][i].in[0] <== and[3][i].out;
multi_or[2][i].in[1] <== and[7][i].out;
states[i+1][1] <== multi_or[2][i].out;
eq[12][i] = IsEqual();
eq[12][i].in[0] <== in[i];
eq[12][i].in[1] <== 13;
and[8][i] = AND();
and[8][i].a <== states[i][0];
and[8][i].b <== eq[12][i].out;
states[i+1][2] <== and[8][i].out;
eq[13][i] = IsEqual();
eq[13][i].in[0] <== in[i];
eq[13][i].in[1] <== 128;
and[9][i] = AND();
and[9][i].a <== states[i][0];
and[9][i].b <== eq[13][i].out;
eq[14][i] = IsEqual();
eq[14][i].in[0] <== in[i];
eq[14][i].in[1] <== 10;
and[10][i] = AND();
and[10][i].a <== states[i][2];
and[10][i].b <== eq[14][i].out;
multi_or[3][i] = MultiOR(2);
multi_or[3][i].in[0] <== and[9][i].out;
multi_or[3][i].in[1] <== and[10][i].out;
states[i+1][3] <== multi_or[3][i].out;
eq[15][i] = IsEqual();
eq[15][i].in[0] <== in[i];
eq[15][i].in[1] <== 62;
and[11][i] = AND();
and[11][i].a <== states[i][1];
and[11][i].b <== eq[15][i].out;
states[i+1][4] <== and[11][i].out;
eq[16][i] = IsEqual();
eq[16][i].in[0] <== in[i];
eq[16][i].in[1] <== 109;
and[12][i] = AND();
and[12][i].a <== states[i][3];
and[12][i].b <== eq[16][i].out;
states[i+1][5] <== and[12][i].out;
eq[17][i] = IsEqual();
eq[17][i].in[0] <== in[i];
eq[17][i].in[1] <== 13;
and[13][i] = AND();
and[13][i].a <== states[i][4];
and[13][i].b <== eq[17][i].out;
states[i+1][6] <== and[13][i].out;
eq[18][i] = IsEqual();
eq[18][i].in[0] <== in[i];
eq[18][i].in[1] <== 10;
and[14][i] = AND();
and[14][i].a <== states[i][6];
and[14][i].b <== eq[18][i].out;
states[i+1][7] <== and[14][i].out;
eq[19][i] = IsEqual();
eq[19][i].in[0] <== in[i];
eq[19][i].in[1] <== 101;
and[15][i] = AND();
and[15][i].a <== states[i][5];
and[15][i].b <== eq[19][i].out;
states[i+1][8] <== and[15][i].out;
eq[20][i] = IsEqual();
eq[20][i].in[0] <== in[i];
eq[20][i].in[1] <== 115;
and[16][i] = AND();
and[16][i].a <== states[i][8];
and[16][i].b <== eq[20][i].out;
states[i+1][9] <== and[16][i].out;
eq[21][i] = IsEqual();
eq[21][i].in[0] <== in[i];
eq[21][i].in[1] <== 115;
and[17][i] = AND();
and[17][i].a <== states[i][9];
and[17][i].b <== eq[21][i].out;
states[i+1][10] <== and[17][i].out;
eq[22][i] = IsEqual();
eq[22][i].in[0] <== in[i];
eq[22][i].in[1] <== 97;
and[18][i] = AND();
and[18][i].a <== states[i][10];
and[18][i].b <== eq[22][i].out;
states[i+1][11] <== and[18][i].out;
eq[23][i] = IsEqual();
eq[23][i].in[0] <== in[i];
eq[23][i].in[1] <== 103;
and[19][i] = AND();
and[19][i].a <== states[i][11];
and[19][i].b <== eq[23][i].out;
states[i+1][12] <== and[19][i].out;
eq[24][i] = IsEqual();
eq[24][i].in[0] <== in[i];
eq[24][i].in[1] <== 101;
and[20][i] = AND();
and[20][i].a <== states[i][12];
and[20][i].b <== eq[24][i].out;
states[i+1][13] <== and[20][i].out;
eq[25][i] = IsEqual();
eq[25][i].in[0] <== in[i];
eq[25][i].in[1] <== 45;
and[21][i] = AND();
and[21][i].a <== states[i][13];
and[21][i].b <== eq[25][i].out;
states[i+1][14] <== and[21][i].out;
eq[26][i] = IsEqual();
eq[26][i].in[0] <== in[i];
eq[26][i].in[1] <== 105;
and[22][i] = AND();
and[22][i].a <== states[i][14];
and[22][i].b <== eq[26][i].out;
states[i+1][15] <== and[22][i].out;
eq[27][i] = IsEqual();
eq[27][i].in[0] <== in[i];
eq[27][i].in[1] <== 100;
and[23][i] = AND();
and[23][i].a <== states[i][15];
and[23][i].b <== eq[27][i].out;
states[i+1][16] <== and[23][i].out;
eq[28][i] = IsEqual();
eq[28][i].in[0] <== in[i];
eq[28][i].in[1] <== 58;
and[24][i] = AND();
and[24][i].a <== states[i][16];
and[24][i].b <== eq[28][i].out;
states[i+1][17] <== and[24][i].out;
eq[29][i] = IsEqual();
eq[29][i].in[0] <== in[i];
eq[29][i].in[1] <== 60;
and[25][i] = AND();
and[25][i].a <== states[i][17];
and[25][i].b <== eq[29][i].out;
states[i+1][18] <== and[25][i].out;
}
signal final_state_sum[num_bytes+1];
final_state_sum[0] <== states[0][7];
for (var i = 1; i <= num_bytes; i++) {
final_state_sum[i] <== final_state_sum[i-1] + states[i][7];
}
out <== final_state_sum[num_bytes];
signal output reveal[msg_bytes];
for (var i = 0; i < msg_bytes; i++) {
reveal[i] <== in[i+1] * (states[i+2][1]);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,7 @@ template WalletSubjectRegex (msg_bytes) {
component multi_or[21][num_bytes]; component multi_or[21][num_bytes];
signal states[num_bytes+1][50]; signal states[num_bytes+1][50];
for (var i = 0; i < num_bytes; i++) { for (var i = 0; i < num_bytes+1; i++) {
states[i][0] <== 1; states[i][0] <== 1;
} }
for (var i = 1; i < 50; i++) { for (var i = 1; i < 50; i++) {

View File

@@ -10,8 +10,6 @@ include "./regexes/from_regex.circom";
include "./regexes/tofrom_domain_regex.circom"; include "./regexes/tofrom_domain_regex.circom";
include "./regexes/body_hash_regex.circom"; include "./regexes/body_hash_regex.circom";
include "./regexes/twitter_reset_regex.circom"; include "./regexes/twitter_reset_regex.circom";
include "./regexes/subject_regex.circom";
// Here, n and k are the biginteger parameters for RSA // Here, n and k are the biginteger parameters for RSA
// This is because the number is chunked into k pack_size of n bits each // This is because the number is chunked into k pack_size of n bits each

View File

@@ -2,14 +2,14 @@
source circuit.env source circuit.env
echo "****MAKE CPP FILE FOR WITNESS GENERATION****" # echo "****MAKE CPP FILE FOR WITNESS GENERATION****"
start=$(date +%s) # start=$(date +%s)
set -x # set -x
make -C "$BUILD_DIR"/"$CIRCUIT_NAME"_cpp/ # make -C "$BUILD_DIR"/"$CIRCUIT_NAME"_cpp/
{ set +x; } 2>/dev/null # { set +x; } 2>/dev/null
end=$(date +%s) # end=$(date +%s)
echo "DONE ($((end - start))s)" # echo "DONE ($((end - start))s)"
echo # echo
# echo "****GENERATING WITNESS FOR SAMPLE INPUT****" # echo "****GENERATING WITNESS FOR SAMPLE INPUT****"
# start=`date +%s` # start=`date +%s`

View File

@@ -0,0 +1,6 @@
module.exports = {
presets: [
['@babel/preset-env', {targets: {node: 'current'}}],
'@babel/preset-typescript',
],
};

View File

@@ -0,0 +1,102 @@
import puppeteer, { Page } from "puppeteer";
import fs from "fs";
import path from "path";
const pageUrl = "https://dev.zkemail.xyz/";
const testEthAddress = "0x00000000000000000000";
const testEmailFilePath = path.join(__dirname, "..", "src", "__fixtures__/email/zktestemail.test-eml");
const testEmailText = fs.readFileSync(testEmailFilePath, "utf8");
// puppeteer test helpers
const emailInputSelector = "textarea[aria-label='Full Email with Headers']";
const ethInputSelector = "input[placeholder='Ethereum Address']";
const proofTextareaSelector = "textarea[aria-label='Proof Output']";
const downloadTimeout = 10000000;
const proofTimeout = 10000000;
const setTextAreaValue = async (page: Page, selector: string, value: string) => {
// This is a workaround for the fact that page.keyboard.type() is too slow.
return await page.$eval(selector, async (element: any, value: string) => {
function setNativeValue(element: any, value: string) {
// @ts-ignore
const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set;
const prototype = Object.getPrototypeOf(element);
// @ts-ignore
const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;
if (valueSetter && valueSetter !== prototypeValueSetter) {
// @ts-ignore
prototypeValueSetter.call(element, value);
} else {
// @ts-ignore
valueSetter.call(element, value);
}
element.dispatchEvent(new Event('input', { bubbles: true }));
}
setNativeValue(element, value);
}, value);
};
const gotToPageAndEnterInputs = async (page: Page, emailInputSelector: string, ethInputSelector: string, testEmailText: string, testEthAddress: string) => {
await page.goto(pageUrl);
await page.waitForSelector(emailInputSelector);
// 'page.keyboard.type()' takes too long. Use workaround.
// await page.focus(emailInputSelector);
// await page.keyboard.type(testEmailText);
await setTextAreaValue(page, emailInputSelector, testEmailText);
await page.waitForSelector(ethInputSelector);
await page.focus(ethInputSelector);
await page.keyboard.type(testEthAddress);
}
describe("App.js", () => {
beforeAll(async () => {
await gotToPageAndEnterInputs(page, emailInputSelector, ethInputSelector, testEmailText, testEthAddress);
}, 60000);
it("should start download and run zkproof after entering inputs and click", async () => {
await page.waitForSelector("[data-testid='status-not-started']");
console.log("starting e2e test...this will take up to 10 minutes and consume bandwidth and cpu time")
const proveButtonSelector = "button[data-testid='prove-button']";
await page.click(proveButtonSelector);
// starting download
console.log("starting download...this will take up to 10 minutes and consume bandwidth");
const proveButtonIsDisabled = await page.$eval(proveButtonSelector, button => (button as HTMLButtonElement).disabled);
expect(proveButtonIsDisabled).toBe(true);
let status;
await page.waitForSelector("[data-testid='status-downloading-proof-files']");
status = await page.$eval("[data-testid='status-downloading-proof-files']", e => e.attributes['data-testid'].value);
expect(status).toBe("status-downloading-proof-files");
await page.waitForSelector("[data-testid='status-generating-proof']", {timeout: downloadTimeout});
console.log("finished download...starting proof");
console.log("starting proof...this will take up to 10 minutes and consume cpu time");
status = await page.$eval("[data-testid='status-generating-proof']", e => e.attributes['data-testid'].value);
expect(status).toBe("status-generating-proof");
await page.waitForSelector("[data-testid='status-done']", {timeout: proofTimeout});
status = await page.$eval("[data-testid='status-done']", e => e.attributes['data-testid'].value);
expect(status).toBe("status-done");
// check proof
const proofValue = await page.$eval(proofTextareaSelector, e => (e as HTMLInputElement).value);
const proofObj = JSON.parse(proofValue);
expect(proofObj["pi_a"]).toBeTruthy();
expect(proofObj["pi_b"]).toBeTruthy();
expect(proofObj["pi_c"]).toBeTruthy();
expect(proofObj["protocol"]).toBe("groth16");
expect(proofObj["curve"]).toBe("bn128");
// report times
const downloadTime = await page.$eval("[data-testid='download-time']", e => e.textContent);
const proofTime = await page.$eval("[data-testid='proof-time']", e => e.textContent);
console.log("Completed download and proof");
console.log("download in ms took", downloadTime);
console.log("proof in ms took", proofTime);
}, proofTimeout + downloadTimeout + 30000);
});

View File

@@ -0,0 +1,80 @@
import puppeteer, { Page } from "puppeteer";
import fs from "fs";
import path from "path";
const pageUrl = "https://dev.zkemail.xyz/";
const testEthAddress = "0x00000000000000000000";
const testEmailFilePath = path.join(__dirname, "..", "src", "__fixtures__/email/zktestemail.test-eml");
const testEmailText = fs.readFileSync(testEmailFilePath, "utf8");
// const testProofFile = "/__fixtures__/proofs/zktestemail.test-proof.json"
// const testProofText = fs.readFileSync(__dirname + testProofFile, "utf8");
// puppeteer test helpers
const emailInputSelector = "textarea[aria-label='Full Email with Headers']";
const ethInputSelector = "input[placeholder='Ethereum Address']";
// const proofTextareaSelector = "textarea[aria-label='Proof Output']";
// const downloadTimeout = 10000000;
// const proofTimeout = 10000000;
const setTextAreaValue = async (page: Page, selector: string, value: string) => {
// This is a workaround for the fact that page.keyboard.type() is too slow.
return await page.$eval(selector, async (element: any, value: string) => {
function setNativeValue(element: any, value: string) {
// @ts-ignore
const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set;
const prototype = Object.getPrototypeOf(element);
// @ts-ignore
const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;
if (valueSetter && valueSetter !== prototypeValueSetter) {
// @ts-ignore
prototypeValueSetter.call(element, value);
} else {
// @ts-ignore
valueSetter.call(element, value);
}
element.dispatchEvent(new Event('input', { bubbles: true }));
}
setNativeValue(element, value);
}, value);
};
const gotToPageAndEnterInputs = async (page: Page, emailInputSelector: string, ethInputSelector: string, testEmailText: string, testEthAddress: string) => {
await page.goto(pageUrl);
await page.waitForSelector(emailInputSelector);
// 'page.keyboard.type()' takes too long. Use workaround.
// await page.focus(emailInputSelector);
// await page.keyboard.type(testEmailText);
await setTextAreaValue(page, emailInputSelector, testEmailText);
await page.waitForSelector(ethInputSelector);
await page.focus(ethInputSelector);
await page.keyboard.type(testEthAddress);
}
describe("App.js", () => {
beforeAll(async () => {
await gotToPageAndEnterInputs(page, emailInputSelector, ethInputSelector, testEmailText, testEthAddress);
}, 60000);
it("should allow email and eth addr to be entered into inputs", async () => {
await page.waitForSelector("[data-testid='status-not-started']");
const emailValue = await page.$eval(emailInputSelector, e => (e as HTMLInputElement).value);
expect(emailValue).toBe(testEmailText);
const ethValue = await page.$eval(ethInputSelector, e => (e as HTMLInputElement).value);
expect(ethValue).toBe(testEthAddress);
});
it("should start with an enabled prove button and status should be 'not-started'", async () => {
await page.waitForSelector("[data-testid='status-not-started']");
const proveButtonSelector = "button[data-testid='prove-button']";
const proveButtonIsDisabled = await page.$eval(proveButtonSelector, button => (button as HTMLButtonElement).disabled);
expect(proveButtonIsDisabled).toBe(false);
const status = await page.$eval("[data-testid='status-not-started']", e => e.attributes['data-testid'].value);
expect(status).toBe("status-not-started");
});
});

View File

@@ -0,0 +1,35 @@
const caps_chrome = {
browserName : 'Chrome',
browserVersion : 'latest',
'LT:Options' : {
platform : 'Windows 10',
build : 'ZK Email Puppeteer-Jest',
name : 'ZK Email Puppeteer-jest test on Chrome',
resolution : '1366x768',
user : process.env.LT_USERNAME,
accessKey : process.env.LT_ACCESS_KEY,
network : true
}
};
const caps_edge = {
browserName : 'MicrosoftEdge',
browserVersion : 'latest',
'LT:Options' : {
platform : 'Windows 10',
build : 'Sample Puppeteer-Jest',
name : 'Puppeteer-jest test on Edge',
resolution : '1366x768',
user : process.env.LT_USERNAME,
accessKey : process.env.LT_ACCESS_KEY,
network : true
}
};
module.exports = {
connect : {
browserWSEndpoint : `wss://cdp.lambdatest.com/puppeteer?capabilities=${encodeURIComponent(
JSON.stringify(caps_chrome)
)}`
}
};

View File

@@ -0,0 +1,9 @@
import type {Config} from 'jest';
export default async (): Promise<Config> => {
return {
verbose: true,
preset: "jest-puppeteer"
};
};

View File

@@ -0,0 +1,17 @@
{
"name": "zk-email-verify-e2e-lambdatest",
"version": "1.0.0",
"description": "Tests for ZK Email on LambdaTest",
"main": "index.js",
"scripts": {
"test": "jest"
},
"author": "",
"license": "MIT",
"devDependencies": {
"@babel/preset-typescript": "^7.21.0",
"@jest/globals": "^29.4.3",
"jest": "^29.4.3",
"jest-puppeteer": "^7.0.1"
}
}

4104
e2e-lambdatest/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,403 @@
Delivered-To: zkemailverify@gmail.com
Received: by 2002:abe:244f:0:b0:351:8511:f81d with SMTP id k76csp1565968vlk;
Sat, 11 Feb 2023 09:22:14 -0800 (PST)
X-Google-Smtp-Source: AK7set/Y3OsAAeTIiMSNbcB9uLcnu/XZVkr0lwa3Lhc02UYddJqHwBqjTSDEn8LEcuk5B2ooL7iz
X-Received: by 2002:a05:690c:b99:b0:52e:9f98:3afb with SMTP id ck25-20020a05690c0b9900b0052e9f983afbmr7390402ywb.8.1676136134009;
Sat, 11 Feb 2023 09:22:14 -0800 (PST)
ARC-Seal: i=1; a=rsa-sha256; t=1676136133; cv=none;
d=google.com; s=arc-20160816;
b=XizR8dC+HfpBsj5NgSZvKF6bGWkJfnE5N8tcK+jWhnn4aC1T8qXM/aPjeKXbasjxVw
EwD0BSIvm28FQ+no5vP1F+/jLaCMB5YvCQBQwadWrzFmhZnYHZ5j/sHeYeAxitbvwzuq
W8soAUmi4jkytC+tbQkHl7FIjAZ8yLgcMnzKWvNYD5VhfSMfr0q0ZJthzX5AjyP47zjk
Noliyk67gPd3ZEeR66wHDVIUaZb9AhExIPfPyg/uO3Qi3vlgoDVYdNYEwY763XK8sMtk
PfZJ9AyarJiBzEUGXMMmaqM3wdxcvAUV8d8EF4HaIU4U1Q0UatOomAGOo1H+JADR0fH/
ZX5Q==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;
h=feedback-id:message-id:mime-version:subject:to:from:date
:dkim-signature;
bh=luw1SBOWXFuYP/jkF1TjW99cBB9mxovOyljNia105q8=;
b=Yw8cjT3S7XHSO4MoOvbKTK/zg5wCWRH91dUpNvyHyQGohemvn6oBL4on6IOZ6wRseC
SJkH6T7facdD6VOFyzdHkGOPTXKPMP90HveULVk7f+57u4aTPpxnRtG6DZlWOqnh/GZl
gvaoWDYrge43sbFMbYd3goNfGuDHOZJbOit+g+GReQ6XBOY2oI7UgYuDtrYR/NB0MBJW
Ed2mr4E3XxX3N+i5fZle4OjfK3xPN+N8huFNaS9x4bOxs8jb+XjRV2vMhadhpmLDRlxN
7iVAWYGz7sa9DOPGKQxh+1cGmlgUJiyeOshHwzK7kKjSFUANfB1qlqZKd7kAc6KYPQ0R
zDcg==
ARC-Authentication-Results: i=1; mx.google.com;
dkim=pass header.i=@twitter.com header.s=dkim-201406 header.b=Mf8VchLe;
spf=pass (google.com: domain of n0399d118e2-e108b1a8a8ec4d32-zkemailverify===gmail.com@bounce.twitter.com designates 199.16.156.166 as permitted sender) smtp.mailfrom="n0399d118e2-e108b1a8a8ec4d32-zkemailverify===gmail.com@bounce.twitter.com";
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=twitter.com
Return-Path: <n0399d118e2-e108b1a8a8ec4d32-zkemailverify===gmail.com@bounce.twitter.com>
Received: from spring-chicken-ba.twitter.com (spring-chicken-ba.twitter.com. [199.16.156.166])
by mx.google.com with ESMTPS id f184-20020a816ac1000000b0050a5b687438si2536384ywc.362.2023.02.11.09.22.13
for <zkemailverify@gmail.com>
(version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128);
Sat, 11 Feb 2023 09:22:13 -0800 (PST)
Received-SPF: pass (google.com: domain of n0399d118e2-e108b1a8a8ec4d32-zkemailverify===gmail.com@bounce.twitter.com designates 199.16.156.166 as permitted sender) client-ip=199.16.156.166;
Authentication-Results: mx.google.com;
dkim=pass header.i=@twitter.com header.s=dkim-201406 header.b=Mf8VchLe;
spf=pass (google.com: domain of n0399d118e2-e108b1a8a8ec4d32-zkemailverify===gmail.com@bounce.twitter.com designates 199.16.156.166 as permitted sender) smtp.mailfrom="n0399d118e2-e108b1a8a8ec4d32-zkemailverify===gmail.com@bounce.twitter.com";
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=twitter.com
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=twitter.com;
s=dkim-201406; t=1676136133;
bh=luw1SBOWXFuYP/jkF1TjW99cBB9mxovOyljNia105q8=;
h=Date:From:To:Subject:MIME-Version:Content-Type:Message-ID;
b=Mf8VchLeAPEOR6FSuqtVMIW0Zut2I+Apy+kLYu93HIzNKlJR2obsh/wirFuTo0qje
KQWpAlyaOGrtWiw7q7Kx7BEZ4wXuKmenccAdNUvS0AedwVeIIgemQHh5ebgw51b1hD
mlZw66c8Q7ffC4cPvGzKM1TYdFt5yNaSSWYLWIoFvQiLL7ePodeYgdT0IMp4kjRl97
sXbdEInGVPYI1g1KkzjhORYI9eBKkZVT4YA/BZZ0WF3fSC1DkF1TecUotNZaDVjB0i
1QxDKumXbQxzmrJ8Z/8jFZ/kaCPKpMSg8RXYzpvaPe635vblMOMPtpIYvRy93vlGxf
ltxUwAkCsug8Q==
X-MSFBL: JEendVXqQSf0QfW2bVHGwxqI6nksIAh1iV1Le82G8P0=|eyJnIjoiQnVsayIsImI
iOiJhdGxhLWJzZy0yOS1zcjEtQnVsay4xODYiLCJyIjoiemtlbWFpbHZlcmlmeUB
nbWFpbC5jb20iLCJ1IjoiemtlbWFpbHZlcmlmeUBnbWFpbC5jb21AaWlkIyNlMTA
4YjFhOGE4ZWM0ZDMyODMwNjdiNTVhMDA5MTJiOUB1c2IjIzI0QDI5NkAxNjI0NDU
3Nzc1ODk0OTg2NzU0QDBAMjRmODhjZTY1YmE4ZWMyNjI2YWZkNjQxZGFkMTY5YzI
yNjJiZjE4NSJ9
Date: Sat, 11 Feb 2023 17:22:13 +0000
From: Twitter <info@twitter.com>
To: zk_practice <zkemailverify@gmail.com>
Subject: Password reset request
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="----=_Part_327915209_1341522477.1676136133617"
X-Twitter-CID: ibis2-password_reset_pin_based_email
Message-ID: <1D.E4.03737.5CEC7E36@twitter.com>
Feedback-ID: atla.c5bbd1c7e491b5023ff4b22a03711997:Twitter
------=_Part_327915209_1341522477.1676136133617
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 7bit
Twitter
Reset your password?
If you requested a password reset for zktestemail, use the confirmation code below to complete the process. If you didn't make this request, ignore this email.
> akfrmhya
Getting a lot of password reset emails?
You can change your account settings to require personal information to reset your password.
account settings
> https://twitter.com/settings/security
------------------------
Help
> https://support.twitter.com/articles/14663
Not my account
> https://twitter.com/account/not_my_account/1624457775894986754/8D785-ED35A-167613?ut=1&amp;cn=cGFzc3dvcmRfcmVzZXRfcGluX2Jhc2VkX2VtYWls
Email security tips
> https://support.twitter.com/articles/204820-fake-twitter-emails
Twitter, Inc. 1355 Market Street, Suite 900 San Francisco, CA 94103
------=_Part_327915209_1341522477.1676136133617
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www=
.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Dutf-8" />
<meta name=3D"viewport" content=3D"width=3Ddevice-width, minimum-scale=3D1.=
0, maximum-scale=3D1.0, user-scalable=3D0" />
<meta name=3D"apple-mobile-web-app-capable" content=3D"yes" />
<style type=3D"text/css">
@media only screen and (max-width: 320px) {
table[class=3D"edu-module"]{
border-radius: 0px !important;
-webkit-border-radius: 0px !important;
-moz-border-radius: 0px !important;
}
td[class=3D"edu-collapse"]{
width: 0px !important;
}
td[class=3D"mobile-height"]{
height: 30px !important;
}
}
@media only screen and (max-width: 420px) {
td[class=3D"spacer"]{
font-size:4px !important;
}
span[class=3D"address"] a {
line-height:18px !important;
}
a[class=3D"cut"]{
display:none !important;
}
td[class=3D"margins"]{
width:18px !important;
}
td[class=3D"edu-margins"]{
width:18px !important;
}
td[class=3D"logo_space"]{
height:12px !important;
}
}
@media only screen and (max-width: 480px) {
table[class=3D"collapse"]{
width:100% !important;
}
table[class=3D"edu-module"]{
width:100% !important;
}
div[class=3D"collapse"]{
width:100% !important;
}
td[class=3D"logo_space"]{
height: 24px !important;
}
span[class=3D"address"]{
display:block !important;
width:240px !important;
}
td[class=3D"cut"]{
display:none !important;
}
td[class=3D"logo"] img {
width:24px !important;
}
span[class=3D"address"] a {
line-height:18px !important;
}
}
</style>
</head>
<body bgcolor=3D"#F5F8FA" style=3D"margin:0;padding:0;-webkit-text-size-adj=
ust:100%;-ms-text-size-adjust:100%;">
<table cellpadding=3D"0" cellspacing=3D"0" border=3D"0" width=3D"100%" bgco=
lor=3D"#F5F8FA" style=3D"background-color:#F5F8FA;padding:0;margin:0;line-h=
eight:1px;font-size:1px;" class=3D"body_wrapper">
<tbody>
<tr>
<td align=3D"center" style=3D"padding:0;margin:0;line-height:1px;font-size:=
1px;">
<table class=3D"collapse" id=3D"header" align=3D"center" width=3D"448" styl=
e=3D"width: 448px;padding:0;margin:0;line-height:1px;font-size:1px;" bgcolo=
r=3D"#ffffff" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
<tbody>
<tr>
<td style=3D"min-width: 448px;padding:0;margin:0;line-height:1px;font-size:=
1px;" class=3D"cut"> <img src=3D"https://ea.twimg.com/email/self_serve/medi=
a/spacer-1402696023930.png" style=3D"min-width: 448px;height:1px;margin:0;p=
adding:0;display:block;-ms-interpolation-mode:bicubic;border:none;outline:n=
one;" /> </td>
</tr>
</tbody>
</table> </td>
</tr>
<tr>
<td align=3D"center" style=3D"padding:0;margin:0;line-height:1px;font-size:=
1px;">
<!--/////////////////// header ///////////////////////////-->
<table class=3D"collapse" id=3D"header" align=3D"center" width=3D"448" styl=
e=3D"width:448px;background-color:#ffffff;padding:0;margin:0;line-height:1p=
x;font-size:1px;" bgcolor=3D"#ffffff" cellpadding=3D"0" cellspacing=3D"0" b=
order=3D"0">
<tbody>
<tr>
<td colspan=3D"4" height=3D"24" style=3D"height:24px;padding:0;margin:0;lin=
e-height:1px;font-size:1px;" class=3D"logo_space"> &nbsp; </td>
</tr>
<tr align=3D"right">
<td width=3D"24" class=3D"margin" style=3D"padding:0;margin:0;line-height:1=
px;font-size:1px;"></td>
<td align=3D"right" style=3D"padding:0;margin:0;line-height:1px;font-size:1=
px;"> <a href=3D"#" target=3D"blank" style=3D"text-decoration:none;border-s=
tyle:none;border:0;padding:0;margin:0;"> <img width=3D"32" align=3D"right" =
src=3D"https://ea.twimg.com/email/self_serve/media/Twitter_logo_180-1468901=
451975.png" style=3D"width:32px;margin:0;padding:0;display:block;-ms-interp=
olation-mode:bicubic;border:none;outline:none;" /> </a> </td>
<td width=3D"24" class=3D"margin" style=3D"padding:0;margin:0;line-height:1=
px;font-size:1px;"></td>
</tr>
<tr>
<td colspan=3D"3" height=3D"24" style=3D"height:24px;padding:0;margin:0;lin=
e-height:1px;font-size:1px;" class=3D"logo_space"> <img width=3D"1" height=
=3D"1" style=3D"display: block;margin:0;padding:0;display:block;-ms-interpo=
lation-mode:bicubic;border:none;outline:none;" src=3D"https://twitter.com/s=
cribe/ibis?t=3D1&amp;cn=3DcGFzc3dvcmRfcmVzZXRfcGluX2Jhc2VkX2VtYWls&amp;iid=
=3De108b1a8a8ec4d3283067b55a00912b9&amp;uid=3D1624457775894986754&amp;nid=
=3D296+20" /> </td>
</tr>
</tbody>
</table>
<!--/////////////////// end header///////////////////////////-->
<!--/////////////////// body ///////////////////////////-->
<table class=3D"collapse" id=3D"header" align=3D"center" width=3D"448" styl=
e=3D"width:448px;background-color:#ffffff;padding:0;margin:0;line-height:1p=
x;font-size:1px;" bgcolor=3D"#ffffff" cellpadding=3D"0" cellspacing=3D"0" b=
order=3D"0">
<tbody>
<tr align=3D"left;">
<td width=3D"24" class=3D"margin" style=3D"padding:0;margin:0;line-height:1=
px;font-size:1px;"></td>
<td align=3D"left;" style=3D"padding:0;margin:0;line-height:1px;font-size:1=
px;">
<table class=3D"collapse" cellpadding=3D"0" cellspacing=3D"0" border=3D"0" =
style=3D"padding:0;margin:0;line-height:1px;font-size:1px;">
<tbody>
<tr>
<td align=3D"left;" class=3D"h2" style=3D"padding:0;margin:0;line-height:1p=
x;font-size:1px;font-family:'HelveticaNeue', 'Helvetica Neue', Helvetica, A=
rial, sans-serif;font-size:24px;line-height:32px;font-weight:bold;color:#29=
2F33;text-align:left;text-decoration:none;-webkit-font-smoothing:antialiase=
d;"> Reset your password? </td>
</tr>
<tr>
<td height=3D"12" style=3D"padding:0;margin:0;line-height:1px;font-size:1px=
;"></td>
</tr>
<tr>
<td align=3D"left;" class=3D"body-text" style=3D"padding:0;margin:0;line-he=
ight:1px;font-size:1px;font-family:'HelveticaNeue', 'Helvetica Neue', Helve=
tica, Arial, sans-serif;font-size:16px;line-height:20px;font-weight:400;col=
or:#292F33;text-align:left;text-decoration:none;-webkit-font-smoothing:anti=
aliased;"> If you requested a password reset for @zktestemail, use the conf=
irmation code below to complete the process. If you didn't make this reques=
t, ignore this email. </td>
</tr>
<tr>
<td height=3D"24" style=3D"padding:0;margin:0;line-height:1px;font-size:1px=
;"></td>
</tr>
<!--*********** password reset pin ************-->
<tr>
<td align=3D"left;" class=3D"support" style=3D"padding:0;margin:0;line-heig=
ht:1px;font-size:1px;font-family:'HelveticaNeue', 'Helvetica Neue', Helveti=
ca, Arial, sans-serif;font-size:14px;line-height:16px;font-weight:400;color=
:#292F33;text-align:left;text-decoration:none;-webkit-font-smoothing:antial=
iased;"> <strong>akfrmhya</strong> </td>
</tr>
<!--*********** end password reset pin ************-->
<tr>
<td height=3D"36" style=3D"height:36px;padding:0;margin:0;line-height:1px;f=
ont-size:1px;"></td>
</tr>
<tr>
<td align=3D"left;" class=3D"body-text" style=3D"padding:0;margin:0;line-he=
ight:1px;font-size:1px;font-family:'HelveticaNeue', 'Helvetica Neue', Helve=
tica, Arial, sans-serif;font-size:16px;line-height:20px;font-weight:400;col=
or:#292F33;text-align:left;text-decoration:none;-webkit-font-smoothing:anti=
aliased;"> <strong>Getting a lot of password reset emails?</strong> </td>
</tr>
<tr>
<td height=3D"12" style=3D"padding:0;margin:0;line-height:1px;font-size:1px=
;"></td>
</tr>
<tr>
<td align=3D"left;" class=3D"body-text" style=3D"padding:0;margin:0;line-he=
ight:1px;font-size:1px;font-family:'HelveticaNeue', 'Helvetica Neue', Helve=
tica, Arial, sans-serif;font-size:16px;line-height:20px;font-weight:400;col=
or:#292F33;text-align:left;text-decoration:none;-webkit-font-smoothing:anti=
aliased;"> You can change your <a href=3D"https://twitter.com/i/redirect?ur=
l=3Dhttps%3A%2F%2Ftwitter.com%2Fsettings%2Fsecurity&amp;t=3D1&amp;cn=3DcGFz=
c3dvcmRfcmVzZXRfcGluX2Jhc2VkX2VtYWls&amp;sig=3D4f2138a21ba47b29174181b08569=
71ac67b6f7f0&amp;iid=3De108b1a8a8ec4d3283067b55a00912b9&amp;uid=3D162445777=
5894986754&amp;nid=3D296+3" style=3D"text-decoration:none;border-style:none=
;border:0;padding:0;margin:0;border:none;text-decoration:none;font-weight:4=
00;color:#1DA1F2;">account settings</a> to require personal information to =
reset your password. </td>
</tr>
<tr>
<td height=3D"36" style=3D"padding:0;margin:0;line-height:1px;font-size:1px=
;"></td>
</tr>
</tbody>
</table> </td>
<td width=3D"24" class=3D"margin" style=3D"padding:0;margin:0;line-height:1=
px;font-size:1px;"></td>
</tr>
</tbody>
</table>
<!--/////////////////// end body///////////////////////////-->
<!--///////////////////// footer /////////////////////-->
<table class=3D"collapse" id=3D"footer" align=3D"center" width=3D"448" styl=
e=3D"width:448px; background-color:#ffffff;padding:0;margin:0;line-height:1=
px;font-size:1px;" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
<tbody>
<tr>
<td height=3D"36" style=3D"height:36px;padding:0;margin:0;line-height:1px;f=
ont-size:1px;"></td>
</tr>
<tr>
<td align=3D"center" style=3D"padding:0;margin:0;line-height:1px;font-size:=
1px;"> <span class=3D"small-copy" style=3D"font-family:'HelveticaNeue', 'He=
lvetica Neue', Helvetica, Arial, sans-serif;font-size:12px;line-height:16px=
;font-weight:400;color:#8899A6;text-align:left;text-decoration:none;-webkit=
-font-smoothing:antialiased;"> <a href=3D"https://support.twitter.com/artic=
les/14663" class=3D"small-copy" style=3D"text-decoration:none;border-style:=
none;border:0;padding:0;margin:0;font-family:'HelveticaNeue', 'Helvetica Ne=
ue', Helvetica, Arial, sans-serif;font-size:12px;line-height:16px;font-weig=
ht:400;color:#8899A6;text-align:left;text-decoration:none;-webkit-font-smoo=
thing:antialiased;font-family:'HelveticaNeue', 'Helvetica Neue', Helvetica,=
Arial, sans-serif;font-size:12px;line-height:16px;font-weight:600;color:#1=
DA1F2;text-align:left;text-decoration:none;-webkit-font-smoothing:antialias=
ed;">Help</a> &nbsp;|&nbsp; <a href=3D"https://twitter.com/account/not_my_a=
ccount/1624457775894986754/8D785-ED35A-167613?ut=3D1&amp;cn=3DcGFzc3dvcmRfc=
mVzZXRfcGluX2Jhc2VkX2VtYWls" class=3D"small-copy" style=3D"text-decoration:=
none;border-style:none;border:0;padding:0;margin:0;font-family:'HelveticaNe=
ue', 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:12px;line-hei=
ght:16px;font-weight:400;color:#8899A6;text-align:left;text-decoration:none=
;-webkit-font-smoothing:antialiased;font-family:'HelveticaNeue', 'Helvetica=
Neue', Helvetica, Arial, sans-serif;font-size:12px;line-height:16px;font-w=
eight:600;color:#1DA1F2;text-align:left;text-decoration:none;-webkit-font-s=
moothing:antialiased;">Not my account</a> &nbsp;|&nbsp; <a href=3D"https://=
twitter.com/i/redirect?url=3Dhttps%3A%2F%2Fsupport.twitter.com%2Farticles%2=
F204820-fake-twitter-emails&amp;t=3D1&amp;cn=3DcGFzc3dvcmRfcmVzZXRfcGluX2Jh=
c2VkX2VtYWls&amp;sig=3D53906e913dd2fe024d96561261d0eed884844e3e&amp;iid=3De=
108b1a8a8ec4d3283067b55a00912b9&amp;uid=3D1624457775894986754&amp;nid=3D296=
+6" class=3D"small-copy" style=3D"text-decoration:none;border-style:none;bo=
rder:0;padding:0;margin:0;font-family:'HelveticaNeue', 'Helvetica Neue', He=
lvetica, Arial, sans-serif;font-size:12px;line-height:16px;font-weight:400;=
color:#8899A6;text-align:left;text-decoration:none;-webkit-font-smoothing:a=
ntialiased;font-family:'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,=
sans-serif;font-size:12px;line-height:16px;font-weight:600;color:#1DA1F2;t=
ext-align:left;text-decoration:none;-webkit-font-smoothing:antialiased;">Em=
ail security tips</a> </span> </td>
</tr>
<tr>
<td height=3D"12" style=3D"height:12px;line-height:1px;font-size:1px;paddin=
g:0;margin:0;line-height:1px;font-size:1px;"></td>
</tr>
<tr>
<td align=3D"center" style=3D"padding:0;margin:0;line-height:1px;font-size:=
1px;"> <span class=3D"small-copy" style=3D"font-family:'HelveticaNeue', 'He=
lvetica Neue', Helvetica, Arial, sans-serif;font-size:12px;line-height:16px=
;font-weight:400;color:#8899A6;text-align:left;text-decoration:none;-webkit=
-font-smoothing:antialiased;"> This email was meant for @zktestemail </span=
> </td>
</tr>
<tr>
<td height=3D"6" style=3D"height:6px;line-height:1px;font-size:1px;padding:=
0;margin:0;line-height:1px;font-size:1px;"></td>
</tr>
<tr>
<td align=3D"center" style=3D"padding:0;margin:0;line-height:1px;font-size:=
1px;"> <span class=3D"address"> <a href=3D"#" style=3D"text-decoration:none=
;border-style:none;border:0;padding:0;margin:0;font-family:'HelveticaNeue',=
'Helvetica Neue', Helvetica, Arial, sans-serif;-webkit-font-smoothing:anti=
aliased;color:#8899A6;font-size:12px;padding:0px;margin:0px;font-weight:nor=
mal;line-height:12px;cursor:default;">Twitter, Inc. 1355 Market Street, Sui=
te 900 San Francisco, CA 94103</a> </span> </td>
</tr>
<tr>
<td height=3D"72" style=3D"height:72px;padding:0;margin:0;line-height:1px;f=
ont-size:1px;"></td>
</tr>
</tbody>
</table>
<!--///////////////////// end footer /////////////////////--> </td>
</tr>
</tbody>
</table>
</body>
</html>
------=_Part_327915209_1341522477.1676136133617--

View File

@@ -1,9 +1,8 @@
{ {
"name": "double-blind", "name": "zk-email",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@openzeppelin/contracts": "^4.8.3",
"@rainbow-me/rainbowkit": "^0.8.0", "@rainbow-me/rainbowkit": "^0.8.0",
"@testing-library/jest-dom": "^5.16.3", "@testing-library/jest-dom": "^5.16.3",
"@testing-library/react": "^12.1.4", "@testing-library/react": "^12.1.4",
@@ -17,6 +16,8 @@
"atob": "^2.1.2", "atob": "^2.1.2",
"base64-sol": "^1.1.0", "base64-sol": "^1.1.0",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"chai": "^4.3.7",
"circom_tester": "^0.0.19",
"circomlibjs": "^0.1.2", "circomlibjs": "^0.1.2",
"cryo": "^0.0.6", "cryo": "^0.0.6",
"crypto-browserify": "^3.12.0", "crypto-browserify": "^3.12.0",
@@ -27,6 +28,7 @@
"libmime": "^5.1.0", "libmime": "^5.1.0",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mocha": "^10.2.0",
"next": "^12.3.1", "next": "^12.3.1",
"node-forge": "^1.3.1", "node-forge": "^1.3.1",
"pako": "^2.1.0", "pako": "^2.1.0",
@@ -42,7 +44,7 @@
"react-use": "^17.3.2", "react-use": "^17.3.2",
"readline": "^1.3.0", "readline": "^1.3.0",
"serve": "^14.0.1", "serve": "^14.0.1",
"snarkjs": "https://github.com/sampritipanda/snarkjs.git#fef81fc51d17a734637555c6edbd585ecda02d9e", "snarkjs": "latest",
"sshpk": "^1.17.0", "sshpk": "^1.17.0",
"styled-components": "^5.3.5", "styled-components": "^5.3.5",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
@@ -64,10 +66,11 @@
"serve": "vite preview", "serve": "vite preview",
"test": "jest --runInBand --testPathIgnorePatterns='e2e' --reporters=default --reporters=jest-junit", "test": "jest --runInBand --testPathIgnorePatterns='e2e' --reporters=default --reporters=jest-junit",
"start-e2e-test-server": "serve -s dist -p 3000", "start-e2e-test-server": "serve -s dist -p 3000",
"test:e2e-ui": "CI=true jest ./e2e-ui*.test.*", "test:e2e-ui": "CI=true jest ./src/e2e-ui*.test.*",
"test:e2e-zkp": "CI=true jest ./e2e-dl-zkp.test.ts", "test:e2e-zkp": "CI=true jest ./src/e2e-dl-zkp.test.ts",
"jest": "jest",
"compile": "node circuits/scripts/compile.js", "compile": "node circuits/scripts/compile.js",
"gen-input": "npx tsx ./scripts/generate_input.ts", "gen-input": "npx tsx ./src/scripts/generate_input.ts",
"compile-all": "yarn gen-input && yarn compile email true", "compile-all": "yarn gen-input && yarn compile email true",
"prepare": "husky install" "prepare": "husky install"
}, },
@@ -97,8 +100,10 @@
"@babel/preset-typescript": "^7.21.5", "@babel/preset-typescript": "^7.21.5",
"@esbuild-plugins/node-globals-polyfill": "^0.2.3", "@esbuild-plugins/node-globals-polyfill": "^0.2.3",
"@esbuild-plugins/node-modules-polyfill": "^0.2.2", "@esbuild-plugins/node-modules-polyfill": "^0.2.2",
"@openzeppelin/contracts": "^4.9.0",
"@types/atob": "^2.1.2", "@types/atob": "^2.1.2",
"@types/jest": "^29.4.0", "@types/jest": "^29.5.1",
"@types/mocha": "^10.0.1",
"@types/node": "^18.0.6", "@types/node": "^18.0.6",
"@types/node-forge": "^1.3.2", "@types/node-forge": "^1.3.2",
"@types/tar-stream": "^2.2.2", "@types/tar-stream": "^2.2.2",

View File

@@ -1,14 +1,24 @@
# ZK Regex # ZK Regex
This code generates a circom regex file with Python and JS, but doesn't support all regex syntax. This code generates a circom regex file with Python and JS, but doesn't support all regex syntax. You have to edit the test_regex function in regex_to_dfa.js to change what is generated.
Note that there is a full JS version of this code with tests at https://github.com/zk-email-verify/zk-regex/ , which also now supports some additional character classes. Once it reaches parity, we expect to update this repo to use that library instead. Note that there is a buggy JS version of this code with tests and a command line tool at https://github.com/zk-email-verify/zk-regex/, which also now supports some additional character classes. Once it reaches parity, we expect to update this repo to use that library instead of gen.py.
Edit the regex on the top of lexical.js to change which regex is generated, then run `python3 gen.py`. Edit the regex on the top of lexical.js to change which regex is generated, then run `python3 gen.py`.
## Halo2 ## Circom Instructions
You can use the compiled halo2_regex_lookup.txt file as input to the https://github.com/zk-email-verify/halo2-regex/ library, which will generate a regex circuit in halo2 instead. That circuit is much more efficient than this one for large inputs. First, generate a regex. Go to our [min_dfa fork](zkregex.com/min_dfa) of cyberzhg's toolbox and insert your regex on the top line. We've forked [min-dfa into a UI here](zkregex.com/min_dfa) to create a UI that converts existing regexes with [] support, as well as escapes \_, and the character classes a-z, A-Z, and 0-9. It also shows the DFA states very clearly so you can choose accept states easily. This should make converting regexes into DFA form way cleaner.
In the function `test_regex()` in `regex_to_dfa.js`, modify either `let raw_regex = ` (that supports some regex strings like `[A-Za-z0-9]` [but no other character ranges]) or modify `let regex = regexToMinDFASpec(<raw regex>)` (that does not support generic brackets or character ranges, only the limited syntax in https://zkregex.com/min_dfa) in `regex_to_circom/regex_to_dfa.js`. The top line of min_dfa tool corresponds to the "raw_regex", and the second line corresponds to the expanded "regex".
Then run `npx tsx regex_to_dfa.js` to make sure that it compiles and `tsx` is installed, and then remove all `console.log` statements except for the last line, and finally run `python3 gen.py`.
This will output a circom body. Wrap it the same way for instance circuits/regexes/from_regex.circom is written. Note that states in the zkregex [min_dfa visualizer](zkregex.com/min_dfa) are now 0 indexed (previous to Apr 2023 you had to subtract 1 from the indexes that showed up to match the circom, now it is the same).
Note that if your regex uses `^` at the start to mean sentinel starting character, you have to edit the resulting regex.circom file to manually change `94` (ascii code of ^) to `128` (manually inserted sentinel character meaning start, you'll see it defined as the 0th character of the string).
We will soon have a website [WIP](https://frontend-zk-regex.vercel.app/) that automatically does this. If you'd like to make this process simpler, cleaner, and less hacky, we'd recommend making a PR here or to the zk-regex library (which is a bit out of date regex-string wise and match group-wise).
## Notes ## Notes
@@ -16,9 +26,15 @@ states[i+1][j] means that there was a character at msg[i] which led to the trans
This means that reveal for index i should be looking at state of index i+1. This means that reveal for index i should be looking at state of index i+1.
Note that ^ has to be manually replaced with \x80 in the circom regex.
## Halo2
You can use the compiled halo2_regex_lookup.txt file as input to the https://github.com/zk-email-verify/halo2-regex/ library, which will generate a regex circuit in halo2 instead. That circuit is more efficient than this one for large inputs for use for fast clientside proofs that require privacy.
## Some regexes ## Some regexes
There are more in the regex section of the top level readme. Here are som examples however, for instance for from/subject/to order-free extraction: There are more in the regex section of the top level zk-email-verify README. Here are some examples however, for instance for from/subject/to order-free extraction:
raw regex: ((\\n|\x80|^)(((from):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>)?|(subject:[a-zA-Z 0-9]+)?|((to):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>)?)(\\r))+ raw regex: ((\\n|\x80|^)(((from):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>)?|(subject:[a-zA-Z 0-9]+)?|((to):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>)?)(\\r))+
min-dfa version: (((\n|^)(((from):([A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|0|1|2|3|4|5|6|7|8|9| |_|.|"|@|-]+<)?[a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|0|1|2|3|4|5|6|7|8|9|_|.|-]+@[a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|0|1|2|3|4|5|6|7|8|9|_|.]+>)?|(subject:[a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z| |0|1|2|3|4|5|6|7|8|9]+)?|((to):([A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|0|1|2|3|4|5|6|7|8|9| |_|.|"|@|-]+<)?[a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|0|1|2|3|4|5|6|7|8|9|_|.|-]+@[a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|0|1|2|3|4|5|6|7|8|9|_|.]+>)?)(\r))+) min-dfa version: (((\n|^)(((from):([A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|0|1|2|3|4|5|6|7|8|9| |_|.|"|@|-]+<)?[a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|0|1|2|3|4|5|6|7|8|9|_|.|-]+@[a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|0|1|2|3|4|5|6|7|8|9|_|.]+>)?|(subject:[a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z| |0|1|2|3|4|5|6|7|8|9]+)?|((to):([A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|0|1|2|3|4|5|6|7|8|9| |_|.|"|@|-]+<)?[a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|0|1|2|3|4|5|6|7|8|9|_|.|-]+@[a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|0|1|2|3|4|5|6|7|8|9|_|.]+>)?)(\r))+)

View File

@@ -139,8 +139,10 @@ function parseRegex(text) {
let new_text = []; let new_text = [];
let i = 0; let i = 0;
while (i < text.length) { while (i < text.length) {
if (text[i] == "\\") { if (text[i] === "\\") {
new_text.push([text[i + 1]]); const escapeMap = { n: "\n", r: "\r", t: "\t", v: "\v", f: "\f", "^": String.fromCharCode(128) };
const char = text[i + 1];
new_text.push([escapeMap[char] || char]);
i += 2; i += 2;
} else { } else {
new_text.push(text[i]); new_text.push(text[i]);

View File

@@ -1,13 +1,14 @@
/*jslint browser: true*/ /*jslint browser: true*/
/*global require, exports*/ /*global require, exports*/
import { assert } from "console";
import { STRING_PRESELECTOR } from "../src/helpers/constants.ts"; import { STRING_PRESELECTOR } from "../src/helpers/constants.ts";
import { minDfa, nfaToDfa, regexToNfa } from "./lexical"; import { minDfa, nfaToDfa, regexToNfa } from "./lexical";
/** This section sets the 'regex' variable to the regex you want to use. /** This section defines helper regex components -- to edit the regex used, edit the return
* of the test_regex function.
* All of the relevant regexes are in the main repo README. * All of the relevant regexes are in the main repo README.
*/ */
// Helper components
const a2z = "a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z"; const a2z = "a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z";
const A2Z = "A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z"; const A2Z = "A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z";
const r0to9 = "0|1|2|3|4|5|6|7|8|9"; const r0to9 = "0|1|2|3|4|5|6|7|8|9";
@@ -23,18 +24,78 @@ const email_chars = `${alphanum}|_|.|-`;
const base_64 = `(${alphanum}|\\+|/|=)`; const base_64 = `(${alphanum}|\\+|/|=)`;
const word_char = `(${alphanum}|_)`; const word_char = `(${alphanum}|_)`;
// let to_from_regex_old = '(\r\n|\x80)(to|from):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>?\r\n';
// let regex = `\r\ndkim-signature:(${key_chars}=${catch_all_without_semicolon}+; )+bh=${base_64}+; `;
// let order_invariant_regex_raw = `((\\n|\x80|^)(((from):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>)?|(subject:[a-zA-Z 0-9]+)?|((to):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>)?|(dkim-signature:((a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z)=(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|!|"|#|$|%|&|\'|\\(|\\)|\\*|\\+|,|-|.|/|:|<|=|>|\\?|@|[|\\\\|]|^|_|`|{|\\||}|~| |\t|\n|\r|\x0B|\f)+; ))?)(\\r))+` // Uses a-z syntax instead of | for each char
const a2z_nosep = "abcdefghijklmnopqrstuvwxyz"; const a2z_nosep = "abcdefghijklmnopqrstuvwxyz";
const A2Z_nosep = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const A2Z_nosep = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const a2f_nosep = "abcdef";
const A2F_nosep = "ABCDEF";
const r0to9_nosep = "0123456789"; const r0to9_nosep = "0123456789";
// Note that in order to specify this string in regex, we must use \\ to escape \'s i.e. in the \r\n // TODO: Note that this is replicated code in lexical.js as well
let order_invariant_header_regex_raw = `(((\\n|^)(((from):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>)?|(subject:[a-zA-Z 0-9]+)?|((to):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>)?)(\\r))+)\\n`; // Note that ^ has to be manually replaced with \x80 in the regex
let sig_regex = `\r\ndkim-signature:(${key_chars}=${catch_all_without_semicolon}+; )+bh=${base_64}+; `; const escapeMap = { n: "\n", r: "\r", t: "\t", v: "\v", f: "\f" };
let whitespace = Object.values(escapeMap);
const slash_s = whitespace.join("|");
// The test_regex function whose return needs to be edited
// Note that in order to specify some strings in regex, we must use \\ to escape \'s.
// For instance, matching the literal + is represented as \\+.
// However, matching the literal \r (ascii 60) character is still \r
// Matching \ then an r as two characters would be \\r in the js string literal
function test_regex() {
// let to_from_regex_old = '(\r\n|\x80)(to|from):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>?\r\n';
// let regex = `\r\ndkim-signature:(${key_chars}=${catch_all_without_semicolon}+; )+bh=${base_64}+; `;
// let order_invariant_regex_raw = `((\\n|\x80|^)(((from):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>)?|(subject:[a-zA-Z 0-9]+)?|((to):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>)?|(dkim-signature:((a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z)=(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|!|"|#|$|%|&|\'|\\(|\\)|\\*|\\+|,|-|.|/|:|<|=|>|\\?|@|[|\\\\|]|^|_|`|{|\\||}|~| |\t|\n|\r|\x0B|\f)+; ))?)(\\r))+` // Uses a-z syntax instead of | for each char
let email_address_regex = `([a-zA-Z0-9._%\\+-=]+@[a-zA-Z0-9.-]+)`;
// ------- HEADER/SIGNATURE REGEX --------
let order_invariant_header_regex_raw = `(((\\n|^)(((from):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>)?|(subject:[a-zA-Z 0-9]+)?|((to):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>)?)(\\r))+)\\n`;
let sig_regex = `\r\ndkim-signature:(${key_chars}=${catch_all_without_semicolon}+; )+bh=${base_64}+; `;
// let full_header_regex = order_invariant_header_regex_raw + sig_regex;
// let raw_regex = order_invariant_header_regex_raw;
// let regex = regexToMinDFASpec(raw_regex) + sig_regex;
// console.log(format_regex_printable(sig_regex));
// -------- SUBJECT REGEXES --------
// This raw subject line (with \\ replaced with \) can be put into regexr.com to test new match strings and sanity check that it works
// TODO: Other valid chars in email addresses: #$%!^/&*, outlined at https://ladedu.com/valid-characters-for-email-addresses-the-complete-list/ and in the RFC
// -- SEND SPECIFIC REGEXES --
// let send_specific_raw_subject_regex = `((\r\n)|^)subject:[Ss]end (\$)?[0-9]+(.[0-9]+)? [a-zA-Z]+ to (${email_address_regex}|0x[0-9a-fA_F]+)\r\n`;
// let raw_subject_regex = `((\r\n)|^)subject:[a-zA-Z]+ (\\$)?[0-9]+(.[0-9]+)? [a-zA-Z]+ to (([a-zA-Z0-9._%\\+-=]+@[a-zA-Z0-9.-]+)|0x[0-9]+)\r\n`;
// Input: ((\\r\\n)|^)subject:[Ss]end (\$)?[0-9]+(.[0-9]+)? [a-zA-Z]+ to (([a-zA-Z0-9._%\+-=]+@[a-zA-Z0-9.-]+)|0x[0-9]+)\\r\\n
// This can be pasted into the first line of https://zkregex.com/min_dfa (after replacing \\ -> \)
// ((\\r\\n)|\^)subject:[Ss]end (\$)?[0-9]+(\.[0-9])? (ETH|DAI|USDC|eth|usdc|dai) to (([a-zA-Z0-9\._%\+-]+@[a-zA-Z0-9\.-]+.[a-zA-Z0-9]+)|0x[0-9]+)\\r\\n
// console.log(raw_subject_regex);
// -- GENERIC SUBJECT COMMANDS --
let raw_subject_regex = `((\r\n)|^)subject:[a-zA-Z]+ (\\$)?[0-9]+(.[0-9]+)? [a-zA-Z]+ to (${email_address_regex}|0x[0-9a-fA_F]+)\r\n`;
// -------- OTHER FIELD REGEXES --------
let raw_from_regex = `(\r\n|^)from:([A-Za-z0-9 _.,"@-]+)<[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.-]+>\r\n`;
// let message_id_regex = `(\r\n|^)message-id:<[=@.\\+_-a-zA-Z0-9]+>\r\n`;
// -------- TWITTER BODY REGEX ---------
// let regex = STRING_PRESELECTOR + `${word_char}+`;
// ---------- DEPRECATAED REGEXES ----------
// let order_invariant_header_regex_raw = `(((\\n|^)(((from):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>)?|(subject:[a-zA-Z 0-9]+)?|((to):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>)?)(\\r))+)`;
// let order_invariant_full_regex_raw = `(dkim-signature:((a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z)=(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|!|"|#|$|%|&|\'|\\(|\\)|\\*|\\+|,|-|.|/|:|<|=|>|\\?|@|[|\\\\|]|^|_|\`|{|\\||}|~| |\t|\n|\r|\x0B|\f)+; ))?)(\\r))+` // Uses a-z syntax instead of | for each char
// let old_regex = '(\r\n|\x80)(to|from):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>?\r\n';
// let regex = `(\n|^)(to|from):((${email_chars}|"|@| )+<)?(${email_chars})+@(${email_chars})+>?\r`;
// let regex = `(\r\n|^)(to|from):((${email_chars}|"|@| )+<)?(${email_chars})+@(${email_chars})+>?\r\n`;
// 'dkim-signature:((a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z)=(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|!|"|#|$|%|&|\'|\\(|\\)|\\*|\\+|,|-|.|/|:|<|=|>|\\?|@|[|\\\\|]|^|_|`|{|\\||}|~| |\t|\n|\r|\x0B|\f)+; )+bh=(a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|0|1|2|3|4|5|6|7|8|9|\\+|/|=)+; '
// let regex = 'hello(0|1|2|3|4|5|6|7|8|9)+world';
// --------- FINAL CONVERSION ---------
// console.log(format_regex_printable(raw_subject_regex));
let regex = regexToMinDFASpec(raw_subject_regex);
// console.log(format_regex_printable(regex));
return regex;
}
// Escapes and prints regexes (might be buggy)
function format_regex_printable(s) { function format_regex_printable(s) {
const escaped_string_json = JSON.stringify(s); const escaped_string_json = JSON.stringify(s);
const escaped_string = escaped_string_json.slice(1, escaped_string_json.length - 1); const escaped_string = escaped_string_json.slice(1, escaped_string_json.length - 1);
@@ -53,10 +114,22 @@ function format_regex_printable(s) {
// let fixed = escaped.replaceAll("\\(", "(").replaceAll("\\)", ")").replaceAll("\\+", "+").replaceAll("\\*", "*").replaceAll("\\?", "?"); // let fixed = escaped.replaceAll("\\(", "(").replaceAll("\\)", ")").replaceAll("\\+", "+").replaceAll("\\*", "*").replaceAll("\\?", "?");
} }
// Note that this is not complete and very case specific i.e. can only handle a-z and not a-c. // Note that this is not complete and very case specific i.e. can only handle a-z and a-f, and not a-c.
// This function expands [] sections to convert values for https://zkregex.com/min_dfa
// The input is a regex with [] and special characters (i.e. the first line of min_dfa tool)
// The output is expanded regexes without any special characters
function regexToMinDFASpec(str) { function regexToMinDFASpec(str) {
// Replace all A-Z with A2Z etc // Replace all A-Z with A2Z etc
let combined_nosep = str.replaceAll("A-Z", A2Z_nosep).replaceAll("a-z", a2z_nosep).replaceAll("0-9", r0to9_nosep); // TODO: Upstream this to min_dfa
let combined_nosep = str
.replaceAll("A-Z", A2Z_nosep)
.replaceAll("a-z", a2z_nosep)
.replaceAll("A-F", A2F_nosep)
.replaceAll("a-f", a2f_nosep)
.replaceAll("0-9", r0to9_nosep)
.replaceAll("\\w", A2Z_nosep + r0to9_nosep + a2z_nosep + "_")
.replaceAll("\\d", r0to9_nosep)
.replaceAll("\\s", slash_s);
// .replaceAll("\\w", A2Z_nosep + r0to9_nosep + a2z_nosep); // I think that there's also an underscore here // .replaceAll("\\w", A2Z_nosep + r0to9_nosep + a2z_nosep); // I think that there's also an underscore here
function addPipeInsideBrackets(str) { function addPipeInsideBrackets(str) {
@@ -99,6 +172,7 @@ function regexToMinDFASpec(str) {
function checkIfBracketsHavePipes(str) { function checkIfBracketsHavePipes(str) {
let result = true; let result = true;
let insideBrackets = false; let insideBrackets = false;
let insideParens = 0;
let indexAt = 0; let indexAt = 0;
for (let i = 0; i < str.length; i++) { for (let i = 0; i < str.length; i++) {
if (indexAt >= str.length) break; if (indexAt >= str.length) break;
@@ -109,6 +183,11 @@ function regexToMinDFASpec(str) {
} else if (str[indexAt] === "]") { } else if (str[indexAt] === "]") {
insideBrackets = false; insideBrackets = false;
} }
if (str[indexAt] === "(") {
insideParens++;
} else if (str[indexAt] === ")") {
insideParens--;
}
if (insideBrackets) { if (insideBrackets) {
if (str[indexAt] === "|") { if (str[indexAt] === "|") {
indexAt++; indexAt++;
@@ -117,6 +196,9 @@ function regexToMinDFASpec(str) {
return result; return result;
} }
} }
if (!insideParens && str[indexAt] === "|") {
console.log("Error: | outside of parens!");
}
if (str[indexAt] === "\\") { if (str[indexAt] === "\\") {
indexAt++; indexAt++;
} }
@@ -129,7 +211,9 @@ function regexToMinDFASpec(str) {
if (!checkIfBracketsHavePipes(combined_nosep)) { if (!checkIfBracketsHavePipes(combined_nosep)) {
// console.log("Adding pipes within brackets between everything!"); // console.log("Adding pipes within brackets between everything!");
combined = addPipeInsideBrackets(combined_nosep); combined = addPipeInsideBrackets(combined_nosep);
assert(checkIfBracketsHavePipes(combined), "Did not add brackets correctly!"); if (!checkIfBracketsHavePipes(combined)) {
console.log("Did not add brackets correctly!");
}
} else { } else {
combined = combined_nosep; combined = combined_nosep;
} }
@@ -137,79 +221,6 @@ function regexToMinDFASpec(str) {
return combined; return combined;
} }
// let full_header_regex = order_invariant_header_regex_raw + sig_regex;
// let raw_regex = order_invariant_header_regex_raw;
// let regex = regexToMinDFASpec(raw_regex) + sig_regex;
// console.log(format_regex_printable(sig_regex));
// This raw subject line (with \\ replaced with \) can be put into regexr.com to test new match strings and sanity check that it works
let email_address_regex = `([a-zA-Z0-9\\._%\\+-]+@[a-zA-Z0-9\\.-]+.[a-zA-Z0-9]+)`;
let raw_subject_regex = `((\r\n)|\^)subject:[Ss]end (\\$)?[0-9]+(\\.[0-9])? (ETH|DAI|USDC|eth|usdc|dai) to (${email_address_regex}|0x[0-9]+)\r\n`;
let raw_from_regex = `(\r\n|^)from:([A-Za-z0-9 _.,"@-]+)<[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.-]+>\r\n`;
// This can be pasted into the first line of zkregex.com/min_dfa
// ((\\r\\n)|\^)subject:[Ss]end (\$)?[0-9]+(\.[0-9])? (ETH|DAI|USDC|eth|usdc|dai) to (([a-zA-Z0-9\._%\+-]+@[a-zA-Z0-9\.-]+.[a-zA-Z0-9]+)|0x[0-9]+)\\r\\n
// console.log(raw_subject_regex);
let regex = regexToMinDFASpec(raw_from_regex);
// This can be pasted into the second line of zkregex.com/min_dfa
// console.log(format_regex_printable(regex));
// TODO" change \^ into \0x80
/*
One indexed! Need to subtract 1 from the DFA state!
// Transition states--
// Amount:
53 2
54 2
2 2
2 4
4 11
// Currency:
3 5
5 13
13 19
3 6
6 14
14 19
3 7
7 15
15 20
20 19
3 8
8 16
16 19
3 9
9 17
17 19
3 10
10 18
18 21
21 19
// Dest states only
// Recipient:
28
34
38
39
26
32
35
40
44
*/
// console.log(raw_regex, "\n", regex);
// let order_invariant_header_regex_raw = `(((\\n|^)(((from):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>)?|(subject:[a-zA-Z 0-9]+)?|((to):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>)?)(\\r))+)`;
// let order_invariant_full_regex_raw = `(dkim-signature:((a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z)=(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|!|"|#|$|%|&|\'|\\(|\\)|\\*|\\+|,|-|.|/|:|<|=|>|\\?|@|[|\\\\|]|^|_|\`|{|\\||}|~| |\t|\n|\r|\x0B|\f)+; ))?)(\\r))+` // Uses a-z syntax instead of | for each char
// let old_regex = '(\r\n|\x80)(to|from):([A-Za-z0-9 _."@-]+<)?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.]+>?\r\n';
// let regex = `(\n|^)(to|from):((${email_chars}|"|@| )+<)?(${email_chars})+@(${email_chars})+>?\r`;
// let regex = `(\r\n|^)(to|from):((${email_chars}|"|@| )+<)?(${email_chars})+@(${email_chars})+>?\r\n`;
// let regex = `\r\ndkim-signature:(${key_chars}=${catch_all_without_semicolon}+; )+bh=${base_64}+; `;
// console.log(regex);
// 'dkim-signature:((a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z)=(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|!|"|#|$|%|&|\'|\\(|\\)|\\*|\\+|,|-|.|/|:|<|=|>|\\?|@|[|\\\\|]|^|_|`|{|\\||}|~| |\t|\n|\r|\x0B|\f)+; )+bh=(a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|0|1|2|3|4|5|6|7|8|9|\\+|/|=)+; '
// let regex = STRING_PRESELECTOR + `${word_char}+`;
// let regex = 'hello(0|1|2|3|4|5|6|7|8|9)+world';
// console.log(regex);
// console.log(Buffer.from(regex).toString('base64'));
function toNature(col) { function toNature(col) {
var i, var i,
j, j,
@@ -225,47 +236,58 @@ function toNature(col) {
return result; return result;
} }
let nfa = regexToNfa(regex); function printGraphForRegex(regex) {
let dfa = minDfa(nfaToDfa(nfa)); let nfa = regexToNfa(regex);
let dfa = minDfa(nfaToDfa(nfa));
var i, var i,
j, j,
states = {}, states = {},
nodes = [], nodes = [],
stack = [dfa], stack = [dfa],
symbols = [], symbols = [],
top; top;
while (stack.length > 0) { while (stack.length > 0) {
top = stack.pop(); top = stack.pop();
if (!states.hasOwnProperty(top.id)) { if (!states.hasOwnProperty(top.id)) {
states[top.id] = top; states[top.id] = top;
top.nature = toNature(top.id); top.nature = toNature(top.id);
nodes.push(top); nodes.push(top);
for (i = 0; i < top.edges.length; i += 1) { for (i = 0; i < top.edges.length; i += 1) {
if (top.edges[i][0] !== "ϵ" && symbols.indexOf(top.edges[i][0]) < 0) { if (top.edges[i][0] !== "ϵ" && symbols.indexOf(top.edges[i][0]) < 0) {
symbols.push(top.edges[i][0]); symbols.push(top.edges[i][0]);
}
stack.push(top.edges[i][1]);
} }
stack.push(top.edges[i][1]);
} }
} }
} nodes.sort(function (a, b) {
nodes.sort(function (a, b) { return a.nature - b.nature;
return a.nature - b.nature; });
}); symbols.sort();
symbols.sort();
let graph = []; let graph = [];
for (let i = 0; i < nodes.length; i += 1) { for (let i = 0; i < nodes.length; i += 1) {
let curr = {}; let curr = {};
curr.type = nodes[i].type; curr.type = nodes[i].type;
curr.edges = {}; curr.edges = {};
for (let j = 0; j < symbols.length; j += 1) { for (let j = 0; j < symbols.length; j += 1) {
if (nodes[i].trans.hasOwnProperty(symbols[j])) { if (nodes[i].trans.hasOwnProperty(symbols[j])) {
curr.edges[symbols[j]] = nodes[i].trans[symbols[j]].nature - 1; curr.edges[symbols[j]] = nodes[i].trans[symbols[j]].nature - 1;
}
} }
graph[nodes[i].nature - 1] = curr;
} }
graph[nodes[i].nature - 1] = curr;
console.log(JSON.stringify(graph));
return JSON.stringify(graph);
} }
console.log(JSON.stringify(graph)); let regex = test_regex();
printGraphForRegex(regex);
if (typeof require === "function") {
exports.regexToMinDFASpec = regexToMinDFASpec;
exports.toNature = toNature;
}

View File

@@ -1,3 +1,11 @@
ALCHEMY_GOERLI_KEY= ALCHEMY_GOERLI_KEY=
INFURA_KEY=
ETHERSCAN_API_KEY= ETHERSCAN_API_KEY=
PRIVATE_KEY= PRIVATE_KEY=
# Prod
# RPC_URL=https://eth-goerli.g.alchemy.com/v2/$ALCHEMY_GOERLI_KEY
RPC_URL=https://goerli.infura.io/v3/$INFURA_KEY
# Dev
# RPC_URL=http://localhost:8548

View File

@@ -11,41 +11,64 @@ To setup,
``` ```
curl -L https://foundry.paradigm.xyz | bash && source ~/.bashrc && foundryup curl -L https://foundry.paradigm.xyz | bash && source ~/.bashrc && foundryup
forge install foundry-rs/forge-std forge install foundry-rs/forge-std
cp node_modules/forge-std src/contracts/lib/forge-std cp -r node_modules/@openzeppelin src/contracts/lib/@openzeppelin
cd src/contracts cd src/contracts
forge install
forge install dapphub/ds-test --no-commit --no-git
``` ```
To test, To test your own contracts, copy TestTwitter.t.sol into a new test file, and make sure you can compile your proof fine. You can run a specific test with `forge test --match test_name`. Then make sure the whole suite passes and isn't above the size limit:
``` ```
forge test forge test --fork-url https://eth-goerli.g.alchemy.com/v2/$ALCHEMY_GOERLI_KEY
forge build --sizes # Make sure these are all below 24kB forge build --sizes # Make sure these are all below 24kB
``` ```
## Deployment ## Deployment
Goerli Address of Deployment: 0xA555F9E05402F8240AC99A0d045081E19C0eB9B3 Goerli Address of Anon Deployment: 0x5c5c7e908ed9904ac9e4f53c8195f95720b9f5c9
Goerli Address of Old Non-Anon Deployment: 0x026343f978d9f5600bf2e05992eb3fff06e4ea80
To deploy contract to local forked mainnet or prod, edit Deploy.s.sol to point to your contracts. You should also edit the `.env` file from cloning ` .env.example` to include your own private key. To deploy contract to local forked mainnet or prod, edit Deploy.s.sol to point to your contracts. In `src/contracts`, you should also edit the `.env` file from cloning `.env.example` to include your own private key.
``` Run local chain in tmux window 1:
# Set terminal to the folder with this README ```bash
cd src/contracts tmux
# Run local chain in tmux window 1 # Run local chain in tmux window 1
export ALCHEMY_GOERLI_KEY=... anvil --fork-url https://eth-goerli.g.alchemy.com/v2/$ALCHEMY_GOERLI_KEY --port 8548
anvil --fork-url https://eth-goerli.g.alchemy.com/v2/$ALCHEMY_GOERLI_KEY --port 8548 # Run in tmux ```
Then deploy the contract to forked goerli:
```bash
# Set terminal to the folder with this README
cd src/contracts
source .env
export MAIN_CONTRACT_NAME=VerifiedTwitterEmail
export RPC_URL="http://127.0.0.1:8548"
# Export to abi for relayers # Export to abi for relayers
forge inspect src/TwitterEmailHandler.sol:$MAIN_CONTRACT_NAME abi --via-ir >> contract.abi forge inspect src/TwitterEmailHandler.sol:$MAIN_CONTRACT_NAME abi >> contract.abi
source .env
# First, test deploy without actually broadcasting it # First, test deploy without actually broadcasting it
forge script script/Deploy.s.sol:Deploy --via-ir -vvvv --rpc-url $RPC_URL forge script script/Deploy.s.sol:Deploy -vvvv --rpc-url $RPC_URL
# Then, actually deploy # Then, actually deploy
forge script script/Deploy.s.sol:Deploy --via-ir -vvvv --rpc-url $RPC_URL --broadcast forge script script/Deploy.s.sol:Deploy -vvvv --rpc-url $RPC_URL --broadcast --slow
# Verify the contract with the raw one via Etherscan # Verify the contract with the raw one via Etherscan
forge verify-contract $EMAIL_ADDR $MAIN_CONTRACT_NAME --watch --etherscan-api-key $GOERLI_ETHERSCAN_API_KEY forge verify-contract $EMAIL_ADDR $MAIN_CONTRACT_NAME --watch --etherscan-api-key $GOERLI_ETHERSCAN_API_KEY
``` ```
### What if I get an error about request failed and not all the contracts deploy?
Maybe fullnode is on [old geth](https://github.com/ethereum/go-ethereum/issues/26890) endpoint, like Alchemy is. Switch to infura or add `--slow` to deploy script:
```
forge script script/Deploy.s.sol:Deploy -vvvv --rpc-url $RPC_URL --broadcast --slow
```
### Versions
10a840db7305d9cdcd1fa56aee88ec77db86a562 is the last stable wallet version before breaking changes to add on-chain anonymity.

View File

@@ -2,5 +2,4 @@
src = 'src' src = 'src'
out = 'out' out = 'out'
libs = ['lib'] # See more config options https://github.com/foundry-rs/foundry/tree/master/config libs = ['lib'] # See more config options https://github.com/foundry-rs/foundry/tree/master/config
remappings = ['ds-test=lib/ds-test/', 'forge-std/=lib/forge-std/']
viaIR = true viaIR = true

View File

@@ -1,656 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity >=0.5.0;
contract DSTest {
event log(string);
event logs(bytes);
event log_address(address);
event log_bytes32(bytes32);
event log_int(int256);
event log_uint(uint256);
event log_bytes(bytes);
event log_string(string);
event log_named_address(string key, address val);
event log_named_bytes32(string key, bytes32 val);
event log_named_decimal_int(string key, int256 val, uint256 decimals);
event log_named_decimal_uint(string key, uint256 val, uint256 decimals);
event log_named_int(string key, int256 val);
event log_named_uint(string key, uint256 val);
event log_named_bytes(string key, bytes val);
event log_named_string(string key, string val);
bool public IS_TEST = true;
bool private _failed;
address constant HEVM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code")))));
modifier mayRevert() {
_;
}
modifier testopts(string memory) {
_;
}
function failed() public returns (bool) {
if (_failed) {
return _failed;
} else {
bool globalFailed = false;
if (hasHEVMContext()) {
(, bytes memory retdata) = HEVM_ADDRESS.call(abi.encodePacked(bytes4(keccak256("load(address,bytes32)")), abi.encode(HEVM_ADDRESS, bytes32("failed"))));
globalFailed = abi.decode(retdata, (bool));
}
return globalFailed;
}
}
function fail() internal {
if (hasHEVMContext()) {
(bool status, ) = HEVM_ADDRESS.call(
abi.encodePacked(bytes4(keccak256("store(address,bytes32,bytes32)")), abi.encode(HEVM_ADDRESS, bytes32("failed"), bytes32(uint256(0x01))))
);
status; // Silence compiler warnings
}
_failed = true;
}
function hasHEVMContext() internal view returns (bool) {
uint256 hevmCodeSize = 0;
assembly {
hevmCodeSize := extcodesize(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)
}
return hevmCodeSize > 0;
}
modifier logs_gas() {
uint256 startGas = gasleft();
_;
uint256 endGas = gasleft();
emit log_named_uint("gas", startGas - endGas);
}
function assertTrue(bool condition) internal {
if (!condition) {
emit log("Error: Assertion Failed");
fail();
}
}
function assertTrue(bool condition, string memory err) internal {
if (!condition) {
emit log_named_string("Error", err);
assertTrue(condition);
}
}
function assertEq(address a, address b) internal {
if (a != b) {
emit log("Error: a == b not satisfied [address]");
emit log_named_address(" Expected", b);
emit log_named_address(" Actual", a);
fail();
}
}
function assertEq(
address a,
address b,
string memory err
) internal {
if (a != b) {
emit log_named_string("Error", err);
assertEq(a, b);
}
}
function assertEq(bytes32 a, bytes32 b) internal {
if (a != b) {
emit log("Error: a == b not satisfied [bytes32]");
emit log_named_bytes32(" Expected", b);
emit log_named_bytes32(" Actual", a);
fail();
}
}
function assertEq(
bytes32 a,
bytes32 b,
string memory err
) internal {
if (a != b) {
emit log_named_string("Error", err);
assertEq(a, b);
}
}
function assertEq32(bytes32 a, bytes32 b) internal {
assertEq(a, b);
}
function assertEq32(
bytes32 a,
bytes32 b,
string memory err
) internal {
assertEq(a, b, err);
}
function assertEq(int256 a, int256 b) internal {
if (a != b) {
emit log("Error: a == b not satisfied [int]");
emit log_named_int(" Expected", b);
emit log_named_int(" Actual", a);
fail();
}
}
function assertEq(
int256 a,
int256 b,
string memory err
) internal {
if (a != b) {
emit log_named_string("Error", err);
assertEq(a, b);
}
}
function assertEq(uint256 a, uint256 b) internal {
if (a != b) {
emit log("Error: a == b not satisfied [uint]");
emit log_named_uint(" Expected", b);
emit log_named_uint(" Actual", a);
fail();
}
}
function assertEq(
uint256 a,
uint256 b,
string memory err
) internal {
if (a != b) {
emit log_named_string("Error", err);
assertEq(a, b);
}
}
function assertEqDecimal(
int256 a,
int256 b,
uint256 decimals
) internal {
if (a != b) {
emit log("Error: a == b not satisfied [decimal int]");
emit log_named_decimal_int(" Expected", b, decimals);
emit log_named_decimal_int(" Actual", a, decimals);
fail();
}
}
function assertEqDecimal(
int256 a,
int256 b,
uint256 decimals,
string memory err
) internal {
if (a != b) {
emit log_named_string("Error", err);
assertEqDecimal(a, b, decimals);
}
}
function assertEqDecimal(
uint256 a,
uint256 b,
uint256 decimals
) internal {
if (a != b) {
emit log("Error: a == b not satisfied [decimal uint]");
emit log_named_decimal_uint(" Expected", b, decimals);
emit log_named_decimal_uint(" Actual", a, decimals);
fail();
}
}
function assertEqDecimal(
uint256 a,
uint256 b,
uint256 decimals,
string memory err
) internal {
if (a != b) {
emit log_named_string("Error", err);
assertEqDecimal(a, b, decimals);
}
}
function assertGt(uint256 a, uint256 b) internal {
if (a <= b) {
emit log("Error: a > b not satisfied [uint]");
emit log_named_uint(" Value a", a);
emit log_named_uint(" Value b", b);
fail();
}
}
function assertGt(
uint256 a,
uint256 b,
string memory err
) internal {
if (a <= b) {
emit log_named_string("Error", err);
assertGt(a, b);
}
}
function assertGt(int256 a, int256 b) internal {
if (a <= b) {
emit log("Error: a > b not satisfied [int]");
emit log_named_int(" Value a", a);
emit log_named_int(" Value b", b);
fail();
}
}
function assertGt(
int256 a,
int256 b,
string memory err
) internal {
if (a <= b) {
emit log_named_string("Error", err);
assertGt(a, b);
}
}
function assertGtDecimal(
int256 a,
int256 b,
uint256 decimals
) internal {
if (a <= b) {
emit log("Error: a > b not satisfied [decimal int]");
emit log_named_decimal_int(" Value a", a, decimals);
emit log_named_decimal_int(" Value b", b, decimals);
fail();
}
}
function assertGtDecimal(
int256 a,
int256 b,
uint256 decimals,
string memory err
) internal {
if (a <= b) {
emit log_named_string("Error", err);
assertGtDecimal(a, b, decimals);
}
}
function assertGtDecimal(
uint256 a,
uint256 b,
uint256 decimals
) internal {
if (a <= b) {
emit log("Error: a > b not satisfied [decimal uint]");
emit log_named_decimal_uint(" Value a", a, decimals);
emit log_named_decimal_uint(" Value b", b, decimals);
fail();
}
}
function assertGtDecimal(
uint256 a,
uint256 b,
uint256 decimals,
string memory err
) internal {
if (a <= b) {
emit log_named_string("Error", err);
assertGtDecimal(a, b, decimals);
}
}
function assertGe(uint256 a, uint256 b) internal {
if (a < b) {
emit log("Error: a >= b not satisfied [uint]");
emit log_named_uint(" Value a", a);
emit log_named_uint(" Value b", b);
fail();
}
}
function assertGe(
uint256 a,
uint256 b,
string memory err
) internal {
if (a < b) {
emit log_named_string("Error", err);
assertGe(a, b);
}
}
function assertGe(int256 a, int256 b) internal {
if (a < b) {
emit log("Error: a >= b not satisfied [int]");
emit log_named_int(" Value a", a);
emit log_named_int(" Value b", b);
fail();
}
}
function assertGe(
int256 a,
int256 b,
string memory err
) internal {
if (a < b) {
emit log_named_string("Error", err);
assertGe(a, b);
}
}
function assertGeDecimal(
int256 a,
int256 b,
uint256 decimals
) internal {
if (a < b) {
emit log("Error: a >= b not satisfied [decimal int]");
emit log_named_decimal_int(" Value a", a, decimals);
emit log_named_decimal_int(" Value b", b, decimals);
fail();
}
}
function assertGeDecimal(
int256 a,
int256 b,
uint256 decimals,
string memory err
) internal {
if (a < b) {
emit log_named_string("Error", err);
assertGeDecimal(a, b, decimals);
}
}
function assertGeDecimal(
uint256 a,
uint256 b,
uint256 decimals
) internal {
if (a < b) {
emit log("Error: a >= b not satisfied [decimal uint]");
emit log_named_decimal_uint(" Value a", a, decimals);
emit log_named_decimal_uint(" Value b", b, decimals);
fail();
}
}
function assertGeDecimal(
uint256 a,
uint256 b,
uint256 decimals,
string memory err
) internal {
if (a < b) {
emit log_named_string("Error", err);
assertGeDecimal(a, b, decimals);
}
}
function assertLt(uint256 a, uint256 b) internal {
if (a >= b) {
emit log("Error: a < b not satisfied [uint]");
emit log_named_uint(" Value a", a);
emit log_named_uint(" Value b", b);
fail();
}
}
function assertLt(
uint256 a,
uint256 b,
string memory err
) internal {
if (a >= b) {
emit log_named_string("Error", err);
assertLt(a, b);
}
}
function assertLt(int256 a, int256 b) internal {
if (a >= b) {
emit log("Error: a < b not satisfied [int]");
emit log_named_int(" Value a", a);
emit log_named_int(" Value b", b);
fail();
}
}
function assertLt(
int256 a,
int256 b,
string memory err
) internal {
if (a >= b) {
emit log_named_string("Error", err);
assertLt(a, b);
}
}
function assertLtDecimal(
int256 a,
int256 b,
uint256 decimals
) internal {
if (a >= b) {
emit log("Error: a < b not satisfied [decimal int]");
emit log_named_decimal_int(" Value a", a, decimals);
emit log_named_decimal_int(" Value b", b, decimals);
fail();
}
}
function assertLtDecimal(
int256 a,
int256 b,
uint256 decimals,
string memory err
) internal {
if (a >= b) {
emit log_named_string("Error", err);
assertLtDecimal(a, b, decimals);
}
}
function assertLtDecimal(
uint256 a,
uint256 b,
uint256 decimals
) internal {
if (a >= b) {
emit log("Error: a < b not satisfied [decimal uint]");
emit log_named_decimal_uint(" Value a", a, decimals);
emit log_named_decimal_uint(" Value b", b, decimals);
fail();
}
}
function assertLtDecimal(
uint256 a,
uint256 b,
uint256 decimals,
string memory err
) internal {
if (a >= b) {
emit log_named_string("Error", err);
assertLtDecimal(a, b, decimals);
}
}
function assertLe(uint256 a, uint256 b) internal {
if (a > b) {
emit log("Error: a <= b not satisfied [uint]");
emit log_named_uint(" Value a", a);
emit log_named_uint(" Value b", b);
fail();
}
}
function assertLe(
uint256 a,
uint256 b,
string memory err
) internal {
if (a > b) {
emit log_named_string("Error", err);
assertLe(a, b);
}
}
function assertLe(int256 a, int256 b) internal {
if (a > b) {
emit log("Error: a <= b not satisfied [int]");
emit log_named_int(" Value a", a);
emit log_named_int(" Value b", b);
fail();
}
}
function assertLe(
int256 a,
int256 b,
string memory err
) internal {
if (a > b) {
emit log_named_string("Error", err);
assertLe(a, b);
}
}
function assertLeDecimal(
int256 a,
int256 b,
uint256 decimals
) internal {
if (a > b) {
emit log("Error: a <= b not satisfied [decimal int]");
emit log_named_decimal_int(" Value a", a, decimals);
emit log_named_decimal_int(" Value b", b, decimals);
fail();
}
}
function assertLeDecimal(
int256 a,
int256 b,
uint256 decimals,
string memory err
) internal {
if (a > b) {
emit log_named_string("Error", err);
assertLeDecimal(a, b, decimals);
}
}
function assertLeDecimal(
uint256 a,
uint256 b,
uint256 decimals
) internal {
if (a > b) {
emit log("Error: a <= b not satisfied [decimal uint]");
emit log_named_decimal_uint(" Value a", a, decimals);
emit log_named_decimal_uint(" Value b", b, decimals);
fail();
}
}
function assertLeDecimal(
uint256 a,
uint256 b,
uint256 decimals,
string memory err
) internal {
if (a > b) {
emit log_named_string("Error", err);
assertGeDecimal(a, b, decimals);
}
}
function assertEq(string memory a, string memory b) internal {
if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) {
emit log("Error: a == b not satisfied [string]");
emit log_named_string(" Expected", b);
emit log_named_string(" Actual", a);
fail();
}
}
function assertEq(
string memory a,
string memory b,
string memory err
) internal {
if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) {
emit log_named_string("Error", err);
assertEq(a, b);
}
}
function checkEq0(bytes memory a, bytes memory b) internal pure returns (bool ok) {
ok = true;
if (a.length == b.length) {
for (uint256 i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
ok = false;
}
}
} else {
ok = false;
}
}
function assertEq0(bytes memory a, bytes memory b) internal {
if (!checkEq0(a, b)) {
emit log("Error: a == b not satisfied [bytes]");
emit log_named_bytes(" Expected", b);
emit log_named_bytes(" Actual", a);
fail();
}
}
function assertEq0(
bytes memory a,
bytes memory b,
string memory err
) internal {
if (!checkEq0(a, b)) {
emit log_named_string("Error", err);
assertEq0(a, b);
}
}
}

View File

@@ -2,3 +2,4 @@
@openzepplin/contracts/=lib/openzeppelin-contracts/contracts/ @openzepplin/contracts/=lib/openzeppelin-contracts/contracts/
@openzepplin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ @openzepplin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
forge-std/=lib/forge-std/src/ forge-std/=lib/forge-std/src/
ds-test=lib/ds-test/src/

View File

@@ -1,28 +0,0 @@
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "forge-std/console.sol";
import "forge-std/Script.sol";
import "../src/TwitterEmailHandler.sol";
import "../src/StringUtils.sol";
import "../src/Groth16VerifierTwitter.sol";
contract Deploy is Script, Test {
function getPrivateKey() internal returns (uint256) {
try vm.envUint("PRIVATE_KEY") returns (uint256 privateKey) {
return privateKey;
} catch {
// This is the anvil default exposed secret key
return 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;
}
}
function run() public {
uint256 sk = getPrivateKey();
vm.startBroadcast(sk);
Verifier proofVerifier = new Verifier();
MailServer mailServer = new MailServer();
VerifiedTwitterEmail testVerifier = new VerifiedTwitterEmail(proofVerifier, mailServer);
vm.stopBroadcast();
}
}

View File

@@ -0,0 +1,27 @@
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "forge-std/console.sol";
import "forge-std/Script.sol";
import "../src/TwitterEmailHandler.sol";
import "../src/Groth16VerifierTwitter.sol";
contract Deploy is Script, Test {
function getPrivateKey() internal returns (uint256) {
try vm.envUint("PRIVATE_KEY") returns (uint256 privateKey) {
return privateKey;
} catch {
// This is the anvil default exposed secret key
return 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;
}
}
function run() public {
uint256 sk = getPrivateKey();
vm.startBroadcast(sk);
Verifier proofVerifier = new Verifier();
MailServer mailServer = new MailServer();
VerifiedTwitterEmail testVerifier = new VerifiedTwitterEmail(proofVerifier, mailServer);
vm.stopBroadcast();
}
}

View File

@@ -1,124 +0,0 @@
pragma solidity ^0.8.0;
import "forge-std/console.sol";
contract MailServer {
uint16 constant rsa_modulus_chunks_len = 17;
mapping(string => uint256[rsa_modulus_chunks_len]) verifiedMailserverKeys;
constructor() {
// Do dig TXT outgoing._domainkey.twitter.com to verify these.
// This is the base 2^121 representation of that key.
// Circom bigint: represent a = a[0] + a[1] * 2**n + .. + a[k - 1] * 2**(n * k)
initMailserverKeys();
}
function initMailserverKeys() internal {
// TODO: Create a type that takes in a raw RSA key, the bit count,
// and whether or not its base64 encoded, and converts it to either 8 or 16 signals
verifiedMailserverKeys["gmail.com"][0] = 1886180949733815343726466520516992271;
verifiedMailserverKeys["gmail.com"][1] = 1551366393280668736485689616947198994;
verifiedMailserverKeys["gmail.com"][2] = 1279057759087427731263511728885611780;
verifiedMailserverKeys["gmail.com"][3] = 1711061746895435768547617398484429347;
verifiedMailserverKeys["gmail.com"][4] = 2329140368326888129406637741054282011;
verifiedMailserverKeys["gmail.com"][5] = 2094858442222190249786465516374057361;
verifiedMailserverKeys["gmail.com"][6] = 2584558507302599829894674874442909655;
verifiedMailserverKeys["gmail.com"][7] = 1521552483858643935889582214011445675;
verifiedMailserverKeys["gmail.com"][8] = 176847449040377757035522930003764000;
verifiedMailserverKeys["gmail.com"][9] = 632921959964166974634188077062540145;
verifiedMailserverKeys["gmail.com"][10] = 2172441457165086627497230906075093832;
verifiedMailserverKeys["gmail.com"][11] = 248112436365636977369105357296082574;
verifiedMailserverKeys["gmail.com"][12] = 1408592841800630696650784801114783401;
verifiedMailserverKeys["gmail.com"][13] = 364610811473321782531041012695979858;
verifiedMailserverKeys["gmail.com"][14] = 342338521965453258686441392321054163;
verifiedMailserverKeys["gmail.com"][15] = 2269703683857229911110544415296249295;
verifiedMailserverKeys["gmail.com"][16] = 3643644972862751728748413716653892;
verifiedMailserverKeys["hotmail.com"][0] = 128339925410438117770406273090474249;
verifiedMailserverKeys["hotmail.com"][1] = 2158906895782814996316644028571725310;
verifiedMailserverKeys["hotmail.com"][2] = 2278019331164769360372919938620729773;
verifiedMailserverKeys["hotmail.com"][3] = 1305319804455735154587383372570664109;
verifiedMailserverKeys["hotmail.com"][4] = 2358345194772578919713586294428642696;
verifiedMailserverKeys["hotmail.com"][5] = 1333692900109074470874155333266985021;
verifiedMailserverKeys["hotmail.com"][6] = 2252956899717870524129098594286063236;
verifiedMailserverKeys["hotmail.com"][7] = 1963190090223950324858653797870319519;
verifiedMailserverKeys["hotmail.com"][8] = 2099240641399560863760865662500577339;
verifiedMailserverKeys["hotmail.com"][9] = 1591320380606901546957315803395187883;
verifiedMailserverKeys["hotmail.com"][10] = 1943831890994545117064894677442719428;
verifiedMailserverKeys["hotmail.com"][11] = 2243327453964709681573059557263184139;
verifiedMailserverKeys["hotmail.com"][12] = 1078181067739519006314708889181549671;
verifiedMailserverKeys["hotmail.com"][13] = 2209638307239559037039565345615684964;
verifiedMailserverKeys["hotmail.com"][14] = 1936371786309180968911326337008120155;
verifiedMailserverKeys["hotmail.com"][15] = 2611115500285740051274748743252547506;
verifiedMailserverKeys["hotmail.com"][16] = 3841983033048617585564391738126779;
verifiedMailserverKeys["ethereum.org"][0] = 119886678941863893035426121053426453;
verifiedMailserverKeys["ethereum.org"][1] = 1819786846289142128062035525540154587;
verifiedMailserverKeys["ethereum.org"][2] = 18664768675154515296388092785538021;
verifiedMailserverKeys["ethereum.org"][3] = 2452916380017370778812419704280324749;
verifiedMailserverKeys["ethereum.org"][4] = 147541693845229442834461965414634823;
verifiedMailserverKeys["ethereum.org"][5] = 714676313158744653841521918164405002;
verifiedMailserverKeys["ethereum.org"][6] = 1495951612535183023869749054624579068;
verifiedMailserverKeys["ethereum.org"][7] = 974892773071523448175479681445882254;
verifiedMailserverKeys["ethereum.org"][8] = 53117264910028079;
verifiedMailserverKeys["ethereum.org"][9] = 0;
verifiedMailserverKeys["ethereum.org"][10] = 0;
verifiedMailserverKeys["ethereum.org"][11] = 0;
verifiedMailserverKeys["ethereum.org"][12] = 0;
verifiedMailserverKeys["ethereum.org"][13] = 0;
verifiedMailserverKeys["ethereum.org"][14] = 0;
verifiedMailserverKeys["ethereum.org"][15] = 0;
verifiedMailserverKeys["ethereum.org"][16] = 0;
verifiedMailserverKeys["twitter.com"][0] = 1634582323953821262989958727173988295;
verifiedMailserverKeys["twitter.com"][1] = 1938094444722442142315201757874145583;
verifiedMailserverKeys["twitter.com"][2] = 375300260153333632727697921604599470;
verifiedMailserverKeys["twitter.com"][3] = 1369658125109277828425429339149824874;
verifiedMailserverKeys["twitter.com"][4] = 1589384595547333389911397650751436647;
verifiedMailserverKeys["twitter.com"][5] = 1428144289938431173655248321840778928;
verifiedMailserverKeys["twitter.com"][6] = 1919508490085653366961918211405731923;
verifiedMailserverKeys["twitter.com"][7] = 2358009612379481320362782200045159837;
verifiedMailserverKeys["twitter.com"][8] = 518833500408858308962881361452944175;
verifiedMailserverKeys["twitter.com"][9] = 1163210548821508924802510293967109414;
verifiedMailserverKeys["twitter.com"][10] = 1361351910698751746280135795885107181;
verifiedMailserverKeys["twitter.com"][11] = 1445969488612593115566934629427756345;
verifiedMailserverKeys["twitter.com"][12] = 2457340995040159831545380614838948388;
verifiedMailserverKeys["twitter.com"][13] = 2612807374136932899648418365680887439;
verifiedMailserverKeys["twitter.com"][14] = 16021263889082005631675788949457422;
verifiedMailserverKeys["twitter.com"][15] = 299744519975649772895460843780023483;
verifiedMailserverKeys["twitter.com"][16] = 3933359104846508935112096715593287;
verifiedMailserverKeys["skiff.com"][0] = 2637270478154147701703365710201556843;
verifiedMailserverKeys["skiff.com"][1] = 2082690054369201099288110516791254232;
verifiedMailserverKeys["skiff.com"][2] = 1108253255381437937379143813840625818;
verifiedMailserverKeys["skiff.com"][3] = 1535554154331979875086566323552212673;
verifiedMailserverKeys["skiff.com"][4] = 273019276149049264013012583938735085;
verifiedMailserverKeys["skiff.com"][5] = 741436192387359949728618527229215889;
verifiedMailserverKeys["skiff.com"][6] = 1851608307869135205473270393049341043;
verifiedMailserverKeys["skiff.com"][7] = 1428718881138594152975742734455140338;
verifiedMailserverKeys["skiff.com"][8] = 778850382237088374273157869416671135;
verifiedMailserverKeys["skiff.com"][9] = 549599381370898291203601849666570597;
verifiedMailserverKeys["skiff.com"][10] = 221161044322752364431317167498442512;
verifiedMailserverKeys["skiff.com"][11] = 2041801755941244198449288035460748224;
verifiedMailserverKeys["skiff.com"][12] = 1083114189020989870026920716001138899;
verifiedMailserverKeys["skiff.com"][13] = 1380362773644527202561949550864154963;
verifiedMailserverKeys["skiff.com"][14] = 1366599807917971505788646146248798329;
verifiedMailserverKeys["skiff.com"][15] = 391565989352979266796804441125988853;
verifiedMailserverKeys["skiff.com"][16] = 3704766395208948862861103932863036;
}
function _stringEq(string memory a, string memory b) internal pure returns (bool) {
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
}
function isVerified(string memory domain, uint256 index, uint256 val) public view returns (bool) {
// allow external queries on mapping
uint256 val1 = verifiedMailserverKeys[domain][index];
uint256 val2 = val;
if (val1 != val2) {
console.log(val1, val2);
}
return verifiedMailserverKeys[domain][index] == val;
}
}

View File

@@ -1,155 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6;
// https://github.com/nalinbhardwaj/ethdosnumber/blob/main/ethdos-contracts/src/HexStrings.sol
library StringUtils {
bytes16 internal constant ALPHABET = "0123456789abcdef";
/// @notice Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
/// @dev Credit to Open Zeppelin under MIT license https://github.com/OpenZeppelin/openzeppelin-contracts/blob/243adff49ce1700e0ecb99fe522fb16cff1d1ddc/contracts/utils/Strings.sol#L55
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = ALPHABET[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length);
for (uint256 i = buffer.length; i > 0; i--) {
buffer[i - 1] = ALPHABET[value & 0xf];
value >>= 4;
}
return string(buffer);
}
function toString(uint256 value) internal pure returns (string memory) {
return toString(abi.encodePacked(value));
}
function toString(bytes32 value) internal pure returns (string memory) {
return toString(abi.encodePacked(value));
}
function toString(address account) internal pure returns (string memory) {
return toString(abi.encodePacked(account));
}
function stringEq(string memory a, string memory b) internal pure returns (bool) {
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
}
function toString(bytes memory data) internal pure returns (string memory) {
bytes memory alphabet = "0123456789abcdef";
bytes memory str = new bytes(2 + data.length * 2);
str[0] = "0";
str[1] = "x";
for (uint256 i = 0; i < data.length; i++) {
str[2 + i * 2] = alphabet[uint256(uint8(data[i] >> 4))];
str[3 + i * 2] = alphabet[uint256(uint8(data[i] & 0x0f))];
}
return string(str);
}
// Unpacks uint256s into bytes and then extracts the non-zero characters
// Only extracts contiguous non-zero characters and ensures theres only 1 such state
// Note that unpackedLen may be more than packedBytes.length * 8 since there may be 0s
// TODO: Remove console.logs and define this as a pure function instead of a view
function convertPackedBytesToBytes(uint256[] memory packedBytes, uint256 maxBytes, uint256 packSize) internal pure returns (string memory extractedString) {
uint8 state = 0;
// bytes: 0 0 0 0 y u s h _ g 0 0 0
// state: 0 0 0 0 1 1 1 1 1 1 2 2 2
bytes memory nonzeroBytesArray = new bytes(packedBytes.length * 7);
uint256 nonzeroBytesArrayIndex = 0;
for (uint16 i = 0; i < packedBytes.length; i++) {
uint256 packedByte = packedBytes[i];
uint8[] memory unpackedBytes = new uint8[](packSize);
for (uint256 j = 0; j < packSize; j++) {
unpackedBytes[j] = uint8(packedByte >> (j * 8));
}
for (uint256 j = 0; j < packSize; j++) {
uint256 unpackedByte = unpackedBytes[j]; //unpackedBytes[j];
// console.log(i, j, state, unpackedByte);
if (unpackedByte != 0) {
nonzeroBytesArray[nonzeroBytesArrayIndex] = bytes1(uint8(unpackedByte));
nonzeroBytesArrayIndex++;
if (state % 2 == 0) {
state += 1;
}
} else {
if (state % 2 == 1) {
state += 1;
}
}
packedByte = packedByte >> 8;
}
}
string memory returnValue = string(nonzeroBytesArray);
require(state >= 1, "Invalid final state of packed bytes in email");
// console.log("Characters in username: ", nonzeroBytesArrayIndex);
require(nonzeroBytesArrayIndex <= maxBytes, "Packed bytes more than allowed max length!");
return returnValue;
// Have to end at the end of the email -- state cannot be 1 since there should be an email footer
}
function bytes32ToString(bytes32 input) internal pure returns (string memory) {
uint256 i;
for (i = 0; i < 32 && input[i] != 0; i++) {}
bytes memory resultBytes = new bytes(i);
for (i = 0; i < 32 && input[i] != 0; i++) {
resultBytes[i] = input[i];
}
return string(resultBytes);
}
// sliceArray is used to slice an array of uint256s from start-end into a new array of uint256s
function sliceArray(uint256[] memory input, uint256 start, uint256 end) internal pure returns (uint256[] memory) {
require(start <= end && end <= input.length, "Invalid slice indices");
uint256[] memory result = new uint256[](end - start);
for (uint256 i = start; i < end; i++) {
result[i - start] = input[i];
}
return result;
}
// stringToUint is used to convert a string like "45" to a uint256 4
function stringToUint(string memory s) internal pure returns (uint256) {
bytes memory b = bytes(s);
uint256 result = 0;
for (uint256 i = 0; i < b.length; i++) {
if (b[i] >= 0x30 && b[i] <= 0x39) {
result = result * 10 + (uint256(uint8(b[i])) - 48);
}
// TODO: Currently truncates decimals
if (b[i] == 0x2E) {
return result;
}
}
return result;
}
// getDomainFromEmail is used to extract the domain from an email i.e. the part after the @
function getDomainFromEmail(string memory fromEmail) internal pure returns (string memory) {
bytes memory emailBytes = bytes(fromEmail);
uint256 atIndex;
for (uint256 i = 0; i < emailBytes.length; i++) {
if (emailBytes[i] == "@") {
atIndex = i;
break;
}
}
bytes memory domainBytes = new bytes(emailBytes.length - atIndex - 1);
for (uint256 j = 0; j < domainBytes.length; j++) {
domainBytes[j] = emailBytes[atIndex + 1 + j];
}
return bytes32ToString(bytes32(bytes(domainBytes)));
}
}

View File

@@ -5,95 +5,101 @@ import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/utils/Strings.sol"; import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Counters.sol"; import "@openzeppelin/contracts/utils/Counters.sol";
import "forge-std/console.sol"; import "forge-std/console.sol";
import "./StringUtils.sol"; import "./utils/StringUtils.sol";
import "./NFTSVG.sol"; import "./utils/NFTSVG.sol";
import { Verifier } from "./Groth16VerifierTwitter.sol"; import {Verifier} from "./Groth16VerifierTwitter.sol";
import "./MailServer.sol"; import "./utils/MailServer.sol";
contract VerifiedTwitterEmail is ERC721Enumerable, Verifier { contract VerifiedTwitterEmail is ERC721Enumerable {
using Counters for Counters.Counter; using Counters for Counters.Counter;
using StringUtils for *; using StringUtils for *;
using NFTSVG for *; using NFTSVG for *;
Counters.Counter private tokenCounter; Counters.Counter private tokenCounter;
uint16 public constant msg_len = 21; // header + body uint16 public constant msg_len = 21; // header + body
uint16 public constant bytesInPackedBytes = 7; // 7 bytes in a packed item returned from circom uint16 public constant bytesInPackedBytes = 7; // 7 bytes in a packed item returned from circom
uint256 public constant body_len = 3; uint256 public constant body_len = 3;
uint256 public constant rsa_modulus_chunks_len = 17; uint256 public constant rsa_modulus_chunks_len = 17;
uint256 public constant header_len = msg_len - body_len; uint256 public constant header_len = msg_len - body_len;
uint256 public constant addressIndexInSignals = msg_len - 1; uint256 public constant addressIndexInSignals = msg_len - 1;
mapping(string => uint256[rsa_modulus_chunks_len]) public verifiedMailserverKeys; mapping(string => uint256[rsa_modulus_chunks_len]) public verifiedMailserverKeys;
mapping(uint256 => string) public tokenIDToName; mapping(uint256 => string) public tokenIDToName;
string constant domain = "twitter.com"; string constant domain = "twitter.com";
MailServer mailServer; MailServer mailServer;
Verifier public immutable verifier; Verifier public immutable verifier;
constructor(Verifier v, MailServer m) ERC721("VerifiedEmail", "VerifiedEmail") { constructor(Verifier v, MailServer m) ERC721("VerifiedEmail", "VerifiedEmail") {
verifier = v; verifier = v;
mailServer = m; mailServer = m;
require(rsa_modulus_chunks_len + body_len + 1 == msg_len, "Variable counts are wrong!"); require(rsa_modulus_chunks_len + body_len + 1 == msg_len, "Variable counts are wrong!");
}
function tokenDesc(uint256 tokenId) public view returns (string memory) {
string memory twitter_username = tokenIDToName[tokenId];
address address_owner = ownerOf(tokenId);
string memory result = string(abi.encodePacked("Twitter username", twitter_username, "is owned by", StringUtils.toString(address_owner)));
return result;
}
function tokenURI(uint256 tokenId) public view override returns (string memory) {
string memory username = tokenIDToName[tokenId];
address owner = ownerOf(tokenId);
return NFTSVG.constructAndReturnSVG(username, tokenId, owner);
}
function _domainCheck(uint256[] memory headerSignals) public pure returns (bool) {
string memory senderBytes = StringUtils.convertPackedBytesToBytes(headerSignals, 18, bytesInPackedBytes);
string[2] memory domainStrings = ["verify@twitter.com", "info@twitter.com"];
return StringUtils.stringEq(senderBytes, domainStrings[0]) || StringUtils.stringEq(senderBytes, domainStrings[1]);
// Usage: require(_domainCheck(senderBytes, domainStrings), "Invalid domain");
}
function mint(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c, uint256[msg_len] memory signals) public {
// TODO no invalid signal check yet, which is fine since the zk proof does it
// Checks: Verify proof and check signals
// require(signals[0] == 1337, "invalid signals");
// 3 public signals are the masked packed message bytes, 17 are the modulus.
uint256[] memory bodySignals = new uint256[](body_len);
uint256[] memory rsaModulusSignals = new uint256[](header_len);
for (uint256 i = 0; i < body_len; i++) {
bodySignals[i] = signals[i];
}
for (uint256 i = body_len; i < msg_len - 1; i++) {
rsaModulusSignals[i - body_len] = signals[i];
} }
// Check eth address committed to in proof matches msg.sender, to avoid replayability function tokenDesc(uint256 tokenId) public view returns (string memory) {
require(address(uint160(signals[addressIndexInSignals])) == msg.sender, "Invalid address"); string memory twitter_username = tokenIDToName[tokenId];
address address_owner = ownerOf(tokenId);
// Check from/to email domains are correct [in this case, only from domain is checked] string memory result = string(
// Right now, we just check that any email was received from anyone at Twitter, which is good enough for now abi.encodePacked("Twitter username", twitter_username, "is owned by", StringUtils.toString(address_owner))
// We will upload the version with these domain checks soon! );
// require(_domainCheck(headerSignals), "Invalid domain"); return result;
// Verify that the public key for RSA matches the hardcoded one
for (uint256 i = body_len; i < msg_len - 1; i++) {
require(mailServer.isVerified(domain, i - body_len, signals[i]), "Invalid: RSA modulus not matched");
} }
require(verifyProof(a, b, c, signals), "Invalid Proof"); // checks effects iteractions, this should come first
// Effects: Mint token function tokenURI(uint256 tokenId) public view override returns (string memory) {
uint256 tokenId = tokenCounter.current() + 1; string memory username = tokenIDToName[tokenId];
string memory messageBytes = StringUtils.convertPackedBytesToBytes(bodySignals, bytesInPackedBytes * body_len, bytesInPackedBytes); address owner = ownerOf(tokenId);
tokenIDToName[tokenId] = messageBytes; return NFTSVG.constructAndReturnSVG(username, tokenId, owner);
_mint(msg.sender, tokenId); }
tokenCounter.increment();
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal { function _domainCheck(uint256[] memory headerSignals) public pure returns (bool) {
require(from == address(0), "Cannot transfer - VerifiedEmail is soulbound"); string memory senderBytes = StringUtils.convertPackedBytesToString(headerSignals, 18, bytesInPackedBytes);
} string[2] memory domainStrings = ["verify@twitter.com", "info@twitter.com"];
return
StringUtils.stringEq(senderBytes, domainStrings[0]) || StringUtils.stringEq(senderBytes, domainStrings[1]);
// Usage: require(_domainCheck(senderBytes, domainStrings), "Invalid domain");
}
function mint(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c, uint256[msg_len] memory signals)
public
{
// TODO no invalid signal check yet, which is fine since the zk proof does it
// Checks: Verify proof and check signals
// require(signals[0] == 1337, "invalid signals");
// 3 public signals are the masked packed message bytes, 17 are the modulus.
uint256[] memory bodySignals = new uint256[](body_len);
uint256[] memory rsaModulusSignals = new uint256[](header_len);
for (uint256 i = 0; i < body_len; i++) {
bodySignals[i] = signals[i];
}
for (uint256 i = body_len; i < msg_len - 1; i++) {
rsaModulusSignals[i - body_len] = signals[i];
}
// Check eth address committed to in proof matches msg.sender, to avoid replayability
require(address(uint160(signals[addressIndexInSignals])) == msg.sender, "Invalid address");
// Check from/to email domains are correct [in this case, only from domain is checked]
// Right now, we just check that any email was received from anyone at Twitter, which is good enough for now
// We will upload the version with these domain checks soon!
// require(_domainCheck(headerSignals), "Invalid domain");
// Verify that the public key for RSA matches the hardcoded one
for (uint256 i = body_len; i < msg_len - 1; i++) {
require(mailServer.isVerified(domain, i - body_len, signals[i]), "Invalid: RSA modulus not matched");
}
require(verifier.verifyProof(a, b, c, signals), "Invalid Proof"); // checks effects iteractions, this should come first
// Effects: Mint token
uint256 tokenId = tokenCounter.current() + 1;
string memory messageBytes =
StringUtils.convertPackedBytesToString(bodySignals, bytesInPackedBytes * body_len, bytesInPackedBytes);
tokenIDToName[tokenId] = messageBytes;
_mint(msg.sender, tokenId);
tokenCounter.increment();
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal {
require(from == address(0), "Cannot transfer - VerifiedEmail is soulbound");
}
} }

View File

@@ -1,7 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import "forge-std/Test.sol"; import "forge-std/Test.sol";
import "../../script/Deploy.s.sol"; import "../../script/DeployTwitter.s.sol";
contract TestDeploy is Test { contract TestDeploy is Test {
Deploy deploy; Deploy deploy;

View File

@@ -6,165 +6,187 @@ import "../TwitterEmailHandler.sol";
import "../Groth16VerifierTwitter.sol"; import "../Groth16VerifierTwitter.sol";
contract TwitterUtilsTest is Test { contract TwitterUtilsTest is Test {
using StringUtils for *; using StringUtils for *;
address constant VM_ADDR = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; // Hardcoded address of the VM from foundry address constant VM_ADDR = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; // Hardcoded address of the VM from foundry
Verifier proofVerifier; Verifier proofVerifier;
MailServer mailServer; MailServer mailServer;
VerifiedTwitterEmail testVerifier; VerifiedTwitterEmail testVerifier;
uint16 public constant packSize = 7; uint16 public constant packSize = 7;
function setUp() public { function setUp() public {
proofVerifier = new Verifier(); proofVerifier = new Verifier();
mailServer = new MailServer(); mailServer = new MailServer();
testVerifier = new VerifiedTwitterEmail(proofVerifier, mailServer); testVerifier = new VerifiedTwitterEmail(proofVerifier, mailServer);
} }
// function testMint() public { // function testMint() public {
// testVerifier.mint // testVerifier.mint
// } // }
// Should pass (note that there are extra 0 bytes, which are filtered out but should be noted in audits) // Should pass (note that there are extra 0 bytes, which are filtered out but should be noted in audits)
function testUnpack1() public { function testUnpack1() public {
uint256[] memory packedBytes = new uint256[](3); uint256[] memory packedBytes = new uint256[](3);
packedBytes[0] = 29096824819513600; packedBytes[0] = 29096824819513600;
packedBytes[1] = 0; packedBytes[1] = 0;
packedBytes[2] = 0; packedBytes[2] = 0;
// This is 0x797573685f670000000000000000000000000000000000000000000000000000 // This is 0x797573685f670000000000000000000000000000000000000000000000000000
// packSize = 7 // packSize = 7
string memory byteList = StringUtils.convertPackedBytesToBytes(packedBytes, 15, packSize); string memory byteList = StringUtils.convertPackedBytesToString(packedBytes, 15, packSize);
// This is 0x797573685f67, since strings are internally arbitrary length arrays // This is 0x797573685f67, since strings are internally arbitrary length arrays
string memory intended_value = "yush_g"; string memory intended_value = "yush_g";
// We need to cast both to bytes32, which works since usernames can be at most 15, alphanumeric + '_' characters // We need to cast both to bytes32, which works since usernames can be at most 15, alphanumeric + '_' characters
// Note that this may not generalize to non-ascii characters. // Note that this may not generalize to non-ascii characters.
// Weird characters are allowed in email addresses, see https://en.wikipedia.org/wiki/Email_address#Local-part // Weird characters are allowed in email addresses, see https://en.wikipedia.org/wiki/Email_address#Local-part
// See https://stackoverflow.com/a/2049510/3977093 -- you can even have international characters with RFC 6532 // See https://stackoverflow.com/a/2049510/3977093 -- you can even have international characters with RFC 6532
// Our regex should just disallow most of these emails, but they may end up taking more than two bytes // Our regex should just disallow most of these emails, but they may end up taking more than two bytes
// ASCII should fit in 2 bytes but emails may not be ASCII // ASCII should fit in 2 bytes but emails may not be ASCII
assertEq(bytes32(bytes(byteList)), bytes32(bytes(intended_value))); assertEq(bytes32(bytes(byteList)), bytes32(bytes(intended_value)));
console.logString(byteList); console.logString(byteList);
} }
function testUnpack2() public { function testUnpack2() public {
uint256[] memory packedBytes = new uint256[](3); uint256[] memory packedBytes = new uint256[](3);
packedBytes[0] = 28557011619965818; packedBytes[0] = 28557011619965818;
packedBytes[1] = 1818845549; packedBytes[1] = 1818845549;
packedBytes[2] = 0; packedBytes[2] = 0;
string memory byteList = StringUtils.convertPackedBytesToBytes(packedBytes, 15, packSize); string memory byteList = StringUtils.convertPackedBytesToString(packedBytes, 15, packSize);
string memory intended_value = "zktestemail"; string memory intended_value = "zktestemail";
assertEq(bytes32(bytes(byteList)), bytes32(bytes(intended_value))); assertEq(bytes32(bytes(byteList)), bytes32(bytes(intended_value)));
console.logString(byteList); console.logString(byteList);
} }
// Should pass (note that there are extra 0 bytes, which are filtered out but should be noted in audits) // Should pass (note that there are extra 0 bytes, which are filtered out but should be noted in audits)
function testVerifyTestEmail() public { function testVerifyTestEmail() public {
uint256[21] memory publicSignals; uint256[21] memory publicSignals;
publicSignals[0] = 28557011619965818; publicSignals[0] = 28557011619965818;
publicSignals[1] = 1818845549; publicSignals[1] = 1818845549;
publicSignals[2] = 0; publicSignals[2] = 0;
publicSignals[3] = 1634582323953821262989958727173988295; publicSignals[3] = 1634582323953821262989958727173988295;
publicSignals[4] = 1938094444722442142315201757874145583; publicSignals[4] = 1938094444722442142315201757874145583;
publicSignals[5] = 375300260153333632727697921604599470; publicSignals[5] = 375300260153333632727697921604599470;
publicSignals[6] = 1369658125109277828425429339149824874; publicSignals[6] = 1369658125109277828425429339149824874;
publicSignals[7] = 1589384595547333389911397650751436647; publicSignals[7] = 1589384595547333389911397650751436647;
publicSignals[8] = 1428144289938431173655248321840778928; publicSignals[8] = 1428144289938431173655248321840778928;
publicSignals[9] = 1919508490085653366961918211405731923; publicSignals[9] = 1919508490085653366961918211405731923;
publicSignals[10] = 2358009612379481320362782200045159837; publicSignals[10] = 2358009612379481320362782200045159837;
publicSignals[11] = 518833500408858308962881361452944175; publicSignals[11] = 518833500408858308962881361452944175;
publicSignals[12] = 1163210548821508924802510293967109414; publicSignals[12] = 1163210548821508924802510293967109414;
publicSignals[13] = 1361351910698751746280135795885107181; publicSignals[13] = 1361351910698751746280135795885107181;
publicSignals[14] = 1445969488612593115566934629427756345; publicSignals[14] = 1445969488612593115566934629427756345;
publicSignals[15] = 2457340995040159831545380614838948388; publicSignals[15] = 2457340995040159831545380614838948388;
publicSignals[16] = 2612807374136932899648418365680887439; publicSignals[16] = 2612807374136932899648418365680887439;
publicSignals[17] = 16021263889082005631675788949457422; publicSignals[17] = 16021263889082005631675788949457422;
publicSignals[18] = 299744519975649772895460843780023483; publicSignals[18] = 299744519975649772895460843780023483;
publicSignals[19] = 3933359104846508935112096715593287; publicSignals[19] = 3933359104846508935112096715593287;
publicSignals[20] = 1; publicSignals[20] = 1;
uint256[2] memory proof_a = [ uint256[2] memory proof_a = [
19927878014774420599335762081097643265718718256586894795640382494403322204498, 19927878014774420599335762081097643265718718256586894795640382494403322204498,
14891682495744632566900850738763676245933032364192093662622519454269163038775 14891682495744632566900850738763676245933032364192093662622519454269163038775
]; ];
// Note: you need to swap the order of the two elements in each subarray // Note: you need to swap the order of the two elements in each subarray
uint256[2][2] memory proof_b = [ uint256[2][2] memory proof_b = [
[14339187615496075499805495539746225674931332745294633353238109947200788411047, 5073539291744271938197991858884932464126239394557616491779032788471213143054], [
[14306493036235766258625927887837901586089699893867499630969317589097278736626, 6852189668501753411744690866678572576357476484128490006857314905430126107219] 14339187615496075499805495539746225674931332745294633353238109947200788411047,
]; 5073539291744271938197991858884932464126239394557616491779032788471213143054
uint256[2] memory proof_c = [ ],
410487323277858030399204852640011945213610339130647561627952237033421360197, [
6281805762334485380296289707165572755690746355667398246912272308643846746573 14306493036235766258625927887837901586089699893867499630969317589097278736626,
]; 6852189668501753411744690866678572576357476484128490006857314905430126107219
]
];
uint256[2] memory proof_c = [
410487323277858030399204852640011945213610339130647561627952237033421360197,
6281805762334485380296289707165572755690746355667398246912272308643846746573
];
// Test proof verification // Test proof verification
bool verified = proofVerifier.verifyProof(proof_a, proof_b, proof_c, publicSignals); bool verified = proofVerifier.verifyProof(proof_a, proof_b, proof_c, publicSignals);
assertEq(verified, true); assertEq(verified, true);
// Test mint after spoofing msg.sender // Test mint after spoofing msg.sender
Vm vm = Vm(VM_ADDR); Vm vm = Vm(VM_ADDR);
vm.startPrank(0x0000000000000000000000000000000000000001); vm.startPrank(0x0000000000000000000000000000000000000001);
testVerifier.mint(proof_a, proof_b, proof_c, publicSignals); testVerifier.mint(proof_a, proof_b, proof_c, publicSignals);
vm.stopPrank(); vm.stopPrank();
} }
// Should pass (note that there are extra 0 bytes, which are filtered out but should be noted in audits) // Should pass (note that there are extra 0 bytes, which are filtered out but should be noted in audits)
function testVerifyYushEmail() public { function testVerifyYushEmail() public {
uint256[21] memory publicSignals; uint256[21] memory publicSignals;
publicSignals[0] = 113659471951225; publicSignals[0] = 113659471951225;
publicSignals[1] = 0; publicSignals[1] = 0;
publicSignals[2] = 0; publicSignals[2] = 0;
publicSignals[3] = 1634582323953821262989958727173988295; publicSignals[3] = 1634582323953821262989958727173988295;
publicSignals[4] = 1938094444722442142315201757874145583; publicSignals[4] = 1938094444722442142315201757874145583;
publicSignals[5] = 375300260153333632727697921604599470; publicSignals[5] = 375300260153333632727697921604599470;
publicSignals[6] = 1369658125109277828425429339149824874; publicSignals[6] = 1369658125109277828425429339149824874;
publicSignals[7] = 1589384595547333389911397650751436647; publicSignals[7] = 1589384595547333389911397650751436647;
publicSignals[8] = 1428144289938431173655248321840778928; publicSignals[8] = 1428144289938431173655248321840778928;
publicSignals[9] = 1919508490085653366961918211405731923; publicSignals[9] = 1919508490085653366961918211405731923;
publicSignals[10] = 2358009612379481320362782200045159837; publicSignals[10] = 2358009612379481320362782200045159837;
publicSignals[11] = 518833500408858308962881361452944175; publicSignals[11] = 518833500408858308962881361452944175;
publicSignals[12] = 1163210548821508924802510293967109414; publicSignals[12] = 1163210548821508924802510293967109414;
publicSignals[13] = 1361351910698751746280135795885107181; publicSignals[13] = 1361351910698751746280135795885107181;
publicSignals[14] = 1445969488612593115566934629427756345; publicSignals[14] = 1445969488612593115566934629427756345;
publicSignals[15] = 2457340995040159831545380614838948388; publicSignals[15] = 2457340995040159831545380614838948388;
publicSignals[16] = 2612807374136932899648418365680887439; publicSignals[16] = 2612807374136932899648418365680887439;
publicSignals[17] = 16021263889082005631675788949457422; publicSignals[17] = 16021263889082005631675788949457422;
publicSignals[18] = 299744519975649772895460843780023483; publicSignals[18] = 299744519975649772895460843780023483;
publicSignals[19] = 3933359104846508935112096715593287; publicSignals[19] = 3933359104846508935112096715593287;
publicSignals[20] = 556307310756571904145052207427031380052712977221; publicSignals[20] = 556307310756571904145052207427031380052712977221;
// TODO switch order // TODO switch order
uint256[2] memory proof_a = [ uint256[2] memory proof_a = [
9363006867611269678582925935753021647889027030446896413835957187406043727690, 9363006867611269678582925935753021647889027030446896413835957187406043727690,
21630169556253404895678159104497446719574525736987888783761908716313881927992 21630169556253404895678159104497446719574525736987888783761908716313881927992
]; ];
// Note: you need to swap the order of the two elements in each subarray // Note: you need to swap the order of the two elements in each subarray
uint256[2][2] memory proof_b = [ uint256[2][2] memory proof_b = [
[16566593201830840943252718762249962483142131594763397873538075518277702645082, 18567659038303546225106951504886253604470228016916658528973206870511276829533], [
[2266080565824575322432873090363833504418041632970946239667340737263413898232, 2242723441612422425510136818011613824051492998493014918147869951941405078798] 16566593201830840943252718762249962483142131594763397873538075518277702645082,
]; 18567659038303546225106951504886253604470228016916658528973206870511276829533
uint256[2] memory proof_c = [ ],
12224501323997049527817799755022184802988108888333268634200461535503052305125, [
3177656185967472916322211236519001250723481802804621893491948147849123768548 2266080565824575322432873090363833504418041632970946239667340737263413898232,
]; 2242723441612422425510136818011613824051492998493014918147869951941405078798
// Test proof verification ]
bool verified = proofVerifier.verifyProof(proof_a, proof_b, proof_c, publicSignals); ];
assertEq(verified, true); uint256[2] memory proof_c = [
12224501323997049527817799755022184802988108888333268634200461535503052305125,
3177656185967472916322211236519001250723481802804621893491948147849123768548
];
// Test proof verification
bool verified = proofVerifier.verifyProof(proof_a, proof_b, proof_c, publicSignals);
assertEq(verified, true);
// Test mint after spoofing msg.sender // Test mint after spoofing msg.sender
Vm vm = Vm(VM_ADDR); Vm vm = Vm(VM_ADDR);
vm.startPrank(0x6171aeBcC9e9B9E1D90EC9C2E124982932297345); vm.startPrank(0x6171aeBcC9e9B9E1D90EC9C2E124982932297345);
testVerifier.mint(proof_a, proof_b, proof_c, publicSignals); testVerifier.mint(proof_a, proof_b, proof_c, publicSignals);
vm.stopPrank(); vm.stopPrank();
} }
function testSVG() public { function testSVG() public {
testVerifyYushEmail(); testVerifyYushEmail();
testVerifyTestEmail(); testVerifyTestEmail();
string memory svgValue = testVerifier.tokenURI(1); string memory svgValue = testVerifier.tokenURI(1);
console.log(svgValue); console.log(svgValue);
assert(bytes(svgValue).length > 0); assert(bytes(svgValue).length > 0);
} }
function testChainID() public view {
uint256 chainId;
assembly {
chainId := chainid()
}
console.log(chainId);
// Local chain, xdai, goerli, mainnet
assert(chainId == 31337 || chainId == 100 || chainId == 5 || chainId == 1);
}
} }

View File

@@ -0,0 +1,129 @@
pragma solidity ^0.8.0;
import "forge-std/console.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MailServer is Ownable {
uint16 constant rsa_modulus_chunks_len = 17;
mapping(string => uint256[rsa_modulus_chunks_len]) verifiedMailserverKeys;
constructor() {
// Do dig TXT outgoing._domainkey.twitter.com to verify these.
// This is the base 2^121 representation of that key.
// Circom bigint: represent a = a[0] + a[1] * 2**n + .. + a[k - 1] * 2**(n * k)
initMailserverKeys();
}
function initMailserverKeys() internal {
// TODO: Create a type that takes in a raw RSA key, the bit count,
// and whether or not its base64 encoded, and converts it to either 8 or 16 signals
verifiedMailserverKeys["gmail.com"][0] = 1886180949733815343726466520516992271;
verifiedMailserverKeys["gmail.com"][1] = 1551366393280668736485689616947198994;
verifiedMailserverKeys["gmail.com"][2] = 1279057759087427731263511728885611780;
verifiedMailserverKeys["gmail.com"][3] = 1711061746895435768547617398484429347;
verifiedMailserverKeys["gmail.com"][4] = 2329140368326888129406637741054282011;
verifiedMailserverKeys["gmail.com"][5] = 2094858442222190249786465516374057361;
verifiedMailserverKeys["gmail.com"][6] = 2584558507302599829894674874442909655;
verifiedMailserverKeys["gmail.com"][7] = 1521552483858643935889582214011445675;
verifiedMailserverKeys["gmail.com"][8] = 176847449040377757035522930003764000;
verifiedMailserverKeys["gmail.com"][9] = 632921959964166974634188077062540145;
verifiedMailserverKeys["gmail.com"][10] = 2172441457165086627497230906075093832;
verifiedMailserverKeys["gmail.com"][11] = 248112436365636977369105357296082574;
verifiedMailserverKeys["gmail.com"][12] = 1408592841800630696650784801114783401;
verifiedMailserverKeys["gmail.com"][13] = 364610811473321782531041012695979858;
verifiedMailserverKeys["gmail.com"][14] = 342338521965453258686441392321054163;
verifiedMailserverKeys["gmail.com"][15] = 2269703683857229911110544415296249295;
verifiedMailserverKeys["gmail.com"][16] = 3643644972862751728748413716653892;
verifiedMailserverKeys["hotmail.com"][0] = 128339925410438117770406273090474249;
verifiedMailserverKeys["hotmail.com"][1] = 2158906895782814996316644028571725310;
verifiedMailserverKeys["hotmail.com"][2] = 2278019331164769360372919938620729773;
verifiedMailserverKeys["hotmail.com"][3] = 1305319804455735154587383372570664109;
verifiedMailserverKeys["hotmail.com"][4] = 2358345194772578919713586294428642696;
verifiedMailserverKeys["hotmail.com"][5] = 1333692900109074470874155333266985021;
verifiedMailserverKeys["hotmail.com"][6] = 2252956899717870524129098594286063236;
verifiedMailserverKeys["hotmail.com"][7] = 1963190090223950324858653797870319519;
verifiedMailserverKeys["hotmail.com"][8] = 2099240641399560863760865662500577339;
verifiedMailserverKeys["hotmail.com"][9] = 1591320380606901546957315803395187883;
verifiedMailserverKeys["hotmail.com"][10] = 1943831890994545117064894677442719428;
verifiedMailserverKeys["hotmail.com"][11] = 2243327453964709681573059557263184139;
verifiedMailserverKeys["hotmail.com"][12] = 1078181067739519006314708889181549671;
verifiedMailserverKeys["hotmail.com"][13] = 2209638307239559037039565345615684964;
verifiedMailserverKeys["hotmail.com"][14] = 1936371786309180968911326337008120155;
verifiedMailserverKeys["hotmail.com"][15] = 2611115500285740051274748743252547506;
verifiedMailserverKeys["hotmail.com"][16] = 3841983033048617585564391738126779;
verifiedMailserverKeys["ethereum.org"][0] = 119886678941863893035426121053426453;
verifiedMailserverKeys["ethereum.org"][1] = 1819786846289142128062035525540154587;
verifiedMailserverKeys["ethereum.org"][2] = 18664768675154515296388092785538021;
verifiedMailserverKeys["ethereum.org"][3] = 2452916380017370778812419704280324749;
verifiedMailserverKeys["ethereum.org"][4] = 147541693845229442834461965414634823;
verifiedMailserverKeys["ethereum.org"][5] = 714676313158744653841521918164405002;
verifiedMailserverKeys["ethereum.org"][6] = 1495951612535183023869749054624579068;
verifiedMailserverKeys["ethereum.org"][7] = 974892773071523448175479681445882254;
verifiedMailserverKeys["ethereum.org"][8] = 53117264910028079;
verifiedMailserverKeys["ethereum.org"][9] = 0;
verifiedMailserverKeys["ethereum.org"][10] = 0;
verifiedMailserverKeys["ethereum.org"][11] = 0;
verifiedMailserverKeys["ethereum.org"][12] = 0;
verifiedMailserverKeys["ethereum.org"][13] = 0;
verifiedMailserverKeys["ethereum.org"][14] = 0;
verifiedMailserverKeys["ethereum.org"][15] = 0;
verifiedMailserverKeys["ethereum.org"][16] = 0;
verifiedMailserverKeys["twitter.com"][0] = 1634582323953821262989958727173988295;
verifiedMailserverKeys["twitter.com"][1] = 1938094444722442142315201757874145583;
verifiedMailserverKeys["twitter.com"][2] = 375300260153333632727697921604599470;
verifiedMailserverKeys["twitter.com"][3] = 1369658125109277828425429339149824874;
verifiedMailserverKeys["twitter.com"][4] = 1589384595547333389911397650751436647;
verifiedMailserverKeys["twitter.com"][5] = 1428144289938431173655248321840778928;
verifiedMailserverKeys["twitter.com"][6] = 1919508490085653366961918211405731923;
verifiedMailserverKeys["twitter.com"][7] = 2358009612379481320362782200045159837;
verifiedMailserverKeys["twitter.com"][8] = 518833500408858308962881361452944175;
verifiedMailserverKeys["twitter.com"][9] = 1163210548821508924802510293967109414;
verifiedMailserverKeys["twitter.com"][10] = 1361351910698751746280135795885107181;
verifiedMailserverKeys["twitter.com"][11] = 1445969488612593115566934629427756345;
verifiedMailserverKeys["twitter.com"][12] = 2457340995040159831545380614838948388;
verifiedMailserverKeys["twitter.com"][13] = 2612807374136932899648418365680887439;
verifiedMailserverKeys["twitter.com"][14] = 16021263889082005631675788949457422;
verifiedMailserverKeys["twitter.com"][15] = 299744519975649772895460843780023483;
verifiedMailserverKeys["twitter.com"][16] = 3933359104846508935112096715593287;
verifiedMailserverKeys["skiff.com"][0] = 2637270478154147701703365710201556843;
verifiedMailserverKeys["skiff.com"][1] = 2082690054369201099288110516791254232;
verifiedMailserverKeys["skiff.com"][2] = 1108253255381437937379143813840625818;
verifiedMailserverKeys["skiff.com"][3] = 1535554154331979875086566323552212673;
verifiedMailserverKeys["skiff.com"][4] = 273019276149049264013012583938735085;
verifiedMailserverKeys["skiff.com"][5] = 741436192387359949728618527229215889;
verifiedMailserverKeys["skiff.com"][6] = 1851608307869135205473270393049341043;
verifiedMailserverKeys["skiff.com"][7] = 1428718881138594152975742734455140338;
verifiedMailserverKeys["skiff.com"][8] = 778850382237088374273157869416671135;
verifiedMailserverKeys["skiff.com"][9] = 549599381370898291203601849666570597;
verifiedMailserverKeys["skiff.com"][10] = 221161044322752364431317167498442512;
verifiedMailserverKeys["skiff.com"][11] = 2041801755941244198449288035460748224;
verifiedMailserverKeys["skiff.com"][12] = 1083114189020989870026920716001138899;
verifiedMailserverKeys["skiff.com"][13] = 1380362773644527202561949550864154963;
verifiedMailserverKeys["skiff.com"][14] = 1366599807917971505788646146248798329;
verifiedMailserverKeys["skiff.com"][15] = 391565989352979266796804441125988853;
verifiedMailserverKeys["skiff.com"][16] = 3704766395208948862861103932863036;
}
function _stringEq(string memory a, string memory b) internal pure returns (bool) {
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
}
function isVerified(string memory domain, uint256 index, uint256 val) public view returns (bool) {
// Allow external queries on mapping
if (verifiedMailserverKeys[domain][index] != val) {
console.log(verifiedMailserverKeys[domain][index], val);
}
return verifiedMailserverKeys[domain][index] == val;
}
function editMailserverKey(string memory domain, uint256 index, uint256 val) public onlyOwner {
verifiedMailserverKeys[domain][index] = val;
}
// TODO: Add DNSSEC verification to add a key as well
}

View File

@@ -0,0 +1,260 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6;
// https://github.com/nalinbhardwaj/ethdosnumber/blob/main/ethdos-contracts/src/HexStrings.sol
library StringUtils {
bytes16 internal constant ALPHABET = "0123456789abcdef";
/// @notice Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
/// @dev Credit to Open Zeppelin under MIT license https://github.com/OpenZeppelin/openzeppelin-contracts/blob/243adff49ce1700e0ecb99fe522fb16cff1d1ddc/contracts/utils/Strings.sol#L55
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = ALPHABET[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length);
for (uint256 i = buffer.length; i > 0; i--) {
buffer[i - 1] = ALPHABET[value & 0xf];
value >>= 4;
}
return string(buffer);
}
function toString(uint256 value) internal pure returns (string memory) {
return toString(abi.encodePacked(value));
}
function toString(bytes32 value) internal pure returns (string memory) {
return toString(abi.encodePacked(value));
}
function toString(address account) internal pure returns (string memory) {
return toString(abi.encodePacked(account));
}
function stringEq(string memory a, string memory b) internal pure returns (bool) {
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
}
function toString(bytes memory data) internal pure returns (string memory) {
bytes memory alphabet = "0123456789abcdef";
bytes memory str = new bytes(2 + data.length * 2);
str[0] = "0";
str[1] = "x";
for (uint256 i = 0; i < data.length; i++) {
str[2 + i * 2] = alphabet[uint256(uint8(data[i] >> 4))];
str[3 + i * 2] = alphabet[uint256(uint8(data[i] & 0x0f))];
}
return string(str);
}
function convertPackedByteToString(uint256 packedByte, uint256 maxBytes, uint256 packSize)
internal
pure
returns (string memory extractedString)
{
uint256[] memory packedBytes = new uint256[](1);
packedBytes[0] = packedByte;
return convertPackedBytesToString(packedBytes, maxBytes, packSize);
}
// Unpacks uint256s into bytes and then extracts the non-zero characters
// Only extracts contiguous non-zero characters and ensures theres only 1 such state
// Note that unpackedLen may be more than packedBytes.length * 8 since there may be 0s
// TODO: Remove console.logs and define this as a pure function instead of a view
function convertPackedBytesToString(uint256[] memory packedBytes, uint256 maxBytes, uint256 packSize)
internal
pure
returns (string memory extractedString)
{
uint8 state = 0;
// bytes: 0 0 0 0 y u s h _ g 0 0 0
// state: 0 0 0 0 1 1 1 1 1 1 2 2 2
bytes memory nonzeroBytesArray = new bytes(packedBytes.length * 7);
uint256 nonzeroBytesArrayIndex = 0;
for (uint16 i = 0; i < packedBytes.length; i++) {
uint256 packedByte = packedBytes[i];
uint8[] memory unpackedBytes = new uint8[](packSize);
for (uint256 j = 0; j < packSize; j++) {
unpackedBytes[j] = uint8(packedByte >> (j * 8));
}
for (uint256 j = 0; j < packSize; j++) {
uint256 unpackedByte = unpackedBytes[j]; //unpackedBytes[j];
// console.log(i, j, state, unpackedByte);
if (unpackedByte != 0) {
nonzeroBytesArray[nonzeroBytesArrayIndex] = bytes1(uint8(unpackedByte));
nonzeroBytesArrayIndex++;
if (state % 2 == 0) {
state += 1;
}
} else {
if (state % 2 == 1) {
state += 1;
}
}
packedByte = packedByte >> 8;
}
}
require(state >= 1, "Invalid final state of packed bytes in email, or value is 0!");
require(nonzeroBytesArrayIndex <= maxBytes, "Packed bytes more than allowed max length!");
string memory returnValue = removeTrailingZeros(string(nonzeroBytesArray));
return returnValue;
// Have to end at the end of the email -- state cannot be 1 since there should be an email footer
}
function bytes32ToString(bytes32 input) internal pure returns (string memory) {
uint256 i;
for (i = 0; i < 32 && input[i] != 0; i++) {}
bytes memory resultBytes = new bytes(i);
for (i = 0; i < 32 && input[i] != 0; i++) {
resultBytes[i] = input[i];
}
return string(resultBytes);
}
// sliceArray is used to slice an array of uint256s from start-end into a new array of uint256s
function sliceArray(uint256[] memory input, uint256 start, uint256 end) internal pure returns (uint256[] memory) {
require(start <= end && end <= input.length, "Invalid slice indices");
uint256[] memory result = new uint256[](end - start);
for (uint256 i = start; i < end; i++) {
result[i - start] = input[i];
}
return result;
}
// stringToUint is used to convert a string like "45" to a uint256 4
function stringToUint(string memory s) internal pure returns (uint256) {
bytes memory b = bytes(s);
uint256 result = 0;
for (uint256 i = 0; i < b.length; i++) {
if (b[i] >= 0x30 && b[i] <= 0x39) {
result = result * 10 + (uint256(uint8(b[i])) - 48);
}
// TODO: Currently truncates decimals
if (b[i] == 0x2E) {
return result;
}
}
return result;
}
// getDomainFromEmail is used to extract the domain from an email i.e. the part after the @
function getDomainFromEmail(string memory fromEmail) internal pure returns (string memory) {
bytes memory emailBytes = bytes(fromEmail);
uint256 atIndex;
for (uint256 i = 0; i < emailBytes.length; i++) {
if (emailBytes[i] == "@") {
atIndex = i;
break;
}
}
bytes memory domainBytes = new bytes(emailBytes.length - atIndex - 1);
for (uint256 j = 0; j < domainBytes.length; j++) {
domainBytes[j] = emailBytes[atIndex + 1 + j];
}
return bytes32ToString(bytes32(bytes(domainBytes)));
}
function removeTrailingZeros(string memory input) public pure returns (string memory) {
bytes memory inputBytes = bytes(input);
uint256 endIndex = inputBytes.length;
for (uint256 i = 0; i < inputBytes.length; i++) {
if (inputBytes[i] == 0) {
endIndex = i;
break;
}
}
bytes memory resultBytes = new bytes(endIndex);
for (uint256 i = 0; i < endIndex; i++) {
resultBytes[i] = inputBytes[i];
}
return string(resultBytes);
}
// Upper/lower string utils from https://github.com/willitscale/solidity-util/blob/master/lib/Strings.sol
/**
* Upper
*
* Converts all the values of a string to their corresponding upper case
* value.
*
* @param _base When being used for a data type this is the extended object
* otherwise this is the string base to convert to upper case
* @return string
*/
function upper(string memory _base) public pure returns (string memory) {
bytes memory _baseBytes = bytes(_base);
for (uint256 i = 0; i < _baseBytes.length; i++) {
_baseBytes[i] = _upper(_baseBytes[i]);
}
return string(_baseBytes);
}
/**
* Lower
*
* Converts all the values of a string to their corresponding lower case
* value.
*
* @param _base When being used for a data type this is the extended object
* otherwise this is the string base to convert to lower case
* @return string
*/
function lower(string memory _base) public pure returns (string memory) {
bytes memory _baseBytes = bytes(_base);
for (uint256 i = 0; i < _baseBytes.length; i++) {
_baseBytes[i] = _lower(_baseBytes[i]);
}
return string(_baseBytes);
}
/**
* Upper
*
* Convert an alphabetic character to upper case and return the original
* value when not alphabetic
*
* @param _b1 The byte to be converted to upper case
* @return bytes1 The converted value if the passed value was alphabetic
* and in a lower case otherwise returns the original value
*/
function _upper(bytes1 _b1) private pure returns (bytes1) {
if (_b1 >= 0x61 && _b1 <= 0x7A) {
return bytes1(uint8(_b1) - 32);
}
return _b1;
}
/**
* Lower
*
* Convert an alphabetic character to lower case and return the original
* value when not alphabetic
*
* @param _b1 The byte to be converted to lower case
* @return bytes1 The converted value if the passed value was alphabetic
* and in a upper case otherwise returns the original value
*/
function _lower(bytes1 _b1) private pure returns (bytes1) {
if (_b1 >= 0x41 && _b1 <= 0x5A) {
return bytes1(uint8(_b1) + 32);
}
return _b1;
}
}

View File

@@ -4,10 +4,11 @@ export function bytesToString(bytes: Uint8Array): string {
return new TextDecoder().decode(bytes); return new TextDecoder().decode(bytes);
} }
// stringToUint8Array
export function stringToBytes(str: string) { export function stringToBytes(str: string) {
const encodedText = new TextEncoder().encode(str); const encodedText = new TextEncoder().encode(str);
const toReturn = Uint8Array.from(str, (x) => x.charCodeAt(0)); const toReturn = Uint8Array.from(str, (x) => x.charCodeAt(0));
const buf = Buffer.from(str, "utf8"); // const buf = Buffer.from(str, "utf8");
return toReturn; return toReturn;
// TODO: Check encoding mismatch if the proof doesnt work // TODO: Check encoding mismatch if the proof doesnt work
// Note that our custom encoding function maps (239, 191, 189) -> (253) // Note that our custom encoding function maps (239, 191, 189) -> (253)

View File

@@ -1,11 +1,3 @@
// the numeric form of the payload1 passed into the primitive
// corresponds to the openssh signature produced by the following command:
// echo "E PLURIBUS UNUM; DO NOT SHARE" | ssh-keygen -Y sign -n double-blind.xyz -f ~/.ssh/id_rsa | pbcopy
export const MAGIC_DOUBLE_BLIND_BASE_MESSAGE =
14447023197094784173331616578829287000074783130802912942914027114823662617007553911501158244718575362051758829289159984830457466395841150324770159971462582912755545324694933673046215187947905307019469n;
// Length in bits
export const MAGIC_DOUBLE_BLIND_BASE_MESSAGE_LEN = 672;
export const CIRCOM_FIELD_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617n; export const CIRCOM_FIELD_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
export const MAX_HEADER_PADDED_BYTES = 1024; // NOTE: this must be the same as the first arg in the email in main args circom export const MAX_HEADER_PADDED_BYTES = 1024; // NOTE: this must be the same as the first arg in the email in main args circom
export const MAX_BODY_PADDED_BYTES = 1536; // NOTE: this must be the same as the arg to sha the remainder number of bytes in the email in main args circom export const MAX_BODY_PADDED_BYTES = 1536; // NOTE: this must be the same as the arg to sha the remainder number of bytes in the email in main args circom

View File

@@ -0,0 +1,9 @@
from Crypto.PublicKey import RSA
from Crypto.Util.number import long_to_bytes
n = 93230141572400662170783753550307761264955081869460053639895434883348985555848023394186640556096911451485975596801521695255694734630678338219590345941037464687540511026989400486063960033279502094209058208866021776512104192812763971526144421706090065402405684981981627715733224518387601434649205998732618652003
e = 65537
key = RSA.construct((n, e))
with open("rsa_pub_key.der", "wb") as f:
f.write(key.exportKey("DER"))

View File

@@ -11,6 +11,7 @@ import {
Uint8ArrayToCharArray, Uint8ArrayToCharArray,
assert, assert,
mergeUInt8Arrays, mergeUInt8Arrays,
int8toBytes,
int64toBytes, int64toBytes,
} from "../helpers/binaryFormat"; } from "../helpers/binaryFormat";
import { CIRCOM_FIELD_MODULUS, MAX_HEADER_PADDED_BYTES, MAX_BODY_PADDED_BYTES, STRING_PRESELECTOR } from "../helpers/constants"; import { CIRCOM_FIELD_MODULUS, MAX_HEADER_PADDED_BYTES, MAX_BODY_PADDED_BYTES, STRING_PRESELECTOR } from "../helpers/constants";
@@ -24,7 +25,7 @@ async function getArgs() {
const emailFileArg = args.find((arg) => arg.startsWith("--email_file=")); const emailFileArg = args.find((arg) => arg.startsWith("--email_file="));
const nonceArg = args.find((arg) => arg.startsWith("--nonce=")); const nonceArg = args.find((arg) => arg.startsWith("--nonce="));
const email_file = emailFileArg ? emailFileArg.split("=")[1] : "test_sendgrid.eml"; const email_file = emailFileArg ? emailFileArg.split("=")[1] : "emls/zktestemail_twitter.eml";
const nonce = nonceArg ? nonceArg.split("=")[1] : null; const nonce = nonceArg ? nonceArg.split("=")[1] : null;
return { email_file, nonce }; return { email_file, nonce };
@@ -47,17 +48,25 @@ export interface ICircuitInputs {
address_plus_one?: string; address_plus_one?: string;
twitter_username_idx?: string; twitter_username_idx?: string;
email_from_idx?: string; email_from_idx?: string;
// subject commands only
command_idx?: string;
message_id_idx?: string;
amount_idx?: string; amount_idx?: string;
currency_idx?: string; currency_idx?: string;
recipient_idx?: string; recipient_idx?: string;
custom_message_id_from?: string[];
custom_message_id_recipient?: string[];
nullifier?: string;
relayer?: string;
} }
enum CircuitType { enum CircuitType {
RSA = "rsa", RSA = "rsa",
SHA = "sha", SHA = "sha",
TEST = "test", TEST = "test",
EMAIL = "email", EMAIL_TWITTER = "email_twitter",
SUBJECTPARSER = "subjectparser", EMAIL_SUBJECT = "email_subject",
} }
async function findSelector(a: Uint8Array, selector: number[]): Promise<number> { async function findSelector(a: Uint8Array, selector: number[]): Promise<number> {
@@ -77,6 +86,24 @@ async function findSelector(a: Uint8Array, selector: number[]): Promise<number>
return -1; return -1;
} }
// Returns the part of str that appears after substr
function trimStrByStr(str: string, substr: string) {
const index = str.indexOf(substr);
if (index === -1) return str;
return str.slice(index + substr.length, str.length);
}
function strToCharArrayStr(str: string) {
return str.split("").map((char) => char.charCodeAt(0).toString());
}
// padWithZero(bodyRemaining, MAX_BODY_PADDED_BYTES)
function padWithZero(arr: Uint8Array, length: number) {
while (arr.length < length) {
arr = mergeUInt8Arrays(arr, int8toBytes(0));
}
return arr;
}
export async function getCircuitInputs( export async function getCircuitInputs(
rsa_signature: BigInt, rsa_signature: BigInt,
rsa_modulus: BigInt, rsa_modulus: BigInt,
@@ -130,9 +157,7 @@ export async function getCircuitInputs(
const bodyRemainingLen = bodyPaddedLen - precomputeText.length; const bodyRemainingLen = bodyPaddedLen - precomputeText.length;
assert(bodyRemainingLen < MAX_BODY_PADDED_BYTES, "Invalid slice"); assert(bodyRemainingLen < MAX_BODY_PADDED_BYTES, "Invalid slice");
assert(bodyRemaining.length % 64 === 0, "Not going to be padded correctly with int64s"); assert(bodyRemaining.length % 64 === 0, "Not going to be padded correctly with int64s");
while (bodyRemaining.length < MAX_BODY_PADDED_BYTES) { bodyRemaining = padWithZero(bodyRemaining, MAX_BODY_PADDED_BYTES);
bodyRemaining = mergeUInt8Arrays(bodyRemaining, int64toBytes(0));
}
assert(bodyRemaining.length === MAX_BODY_PADDED_BYTES, "Invalid slice"); assert(bodyRemaining.length === MAX_BODY_PADDED_BYTES, "Invalid slice");
const bodyShaPrecompute = await partialSha(precomputeText, shaCutoffIndex); const bodyShaPrecompute = await partialSha(precomputeText, shaCutoffIndex);
@@ -150,26 +175,16 @@ export async function getCircuitInputs(
const body_hash_idx = bufferToString(message).indexOf(body_hash).toString(); const body_hash_idx = bufferToString(message).indexOf(body_hash).toString();
const address = bytesToBigInt(fromHex(eth_address)).toString(); const address = bytesToBigInt(fromHex(eth_address)).toString();
const nullifier = signature[0];
// bytesToBigInt(fromHex()).toString();
const address_plus_one = (bytesToBigInt(fromHex(eth_address)) + 1n).toString(); const address_plus_one = (bytesToBigInt(fromHex(eth_address)) + 1n).toString();
const USERNAME_SELECTOR = Buffer.from(STRING_PRESELECTOR); const USERNAME_SELECTOR = Buffer.from(STRING_PRESELECTOR);
function trimStrByStr(str: string, substr: string) {
const index = str.indexOf(substr);
if (index === -1) {
return str;
}
return str.slice(index + substr.length, str.length);
}
let raw_header = Buffer.from(prehash_message_string).toString(); let raw_header = Buffer.from(prehash_message_string).toString();
const email_from_idx = raw_header.length - trimStrByStr(trimStrByStr(raw_header, "from:"), "<").length; const email_from_idx = raw_header.length - trimStrByStr(trimStrByStr(raw_header, "from:"), "<").length;
let email_subject = trimStrByStr(raw_header, "subject:"); let email_subject = trimStrByStr(raw_header, "\r\nsubject:");
const amount_idx = raw_header.length - trimStrByStr(email_subject, "end ").length; //in javascript, give me a function that extracts the first word in a string, everything before the first space
const currency_idx = raw_header.length - trimStrByStr(trimStrByStr(email_subject, "end "), " ").length;
const recipient_idx = raw_header.length - trimStrByStr(email_subject, "to ").length;
const twitter_username_idx = (Buffer.from(bodyRemaining).indexOf(USERNAME_SELECTOR) + USERNAME_SELECTOR.length).toString();
console.log("Indexes into header string are: ", email_from_idx, amount_idx, currency_idx, recipient_idx, twitter_username_idx);
if (circuit === CircuitType.RSA) { if (circuit === CircuitType.RSA) {
circuitInputs = { circuitInputs = {
@@ -177,7 +192,10 @@ export async function getCircuitInputs(
signature, signature,
base_message, base_message,
}; };
} else if (circuit === CircuitType.EMAIL) { } else if (circuit === CircuitType.EMAIL_TWITTER) {
const twitter_username_idx = (Buffer.from(bodyRemaining).indexOf(USERNAME_SELECTOR) + USERNAME_SELECTOR.length).toString();
console.log("Indexes into header string are: ", email_from_idx, twitter_username_idx);
circuitInputs = { circuitInputs = {
in_padded, in_padded,
modulus, modulus,
@@ -192,19 +210,39 @@ export async function getCircuitInputs(
body_hash_idx, body_hash_idx,
// email_from_idx, // email_from_idx,
}; };
} else if (circuit === CircuitType.SUBJECTPARSER) { } else if (circuit === CircuitType.EMAIL_SUBJECT) {
// First word after "subject:" (usually send/Send)
const command = email_subject.split(" ")[0];
const command_idx = raw_header.length - email_subject.length;
// Index of first word after command
const amount_idx = raw_header.length - trimStrByStr(email_subject, command).length;
// Index of second word after command
const currency_idx = raw_header.length - trimStrByStr(trimStrByStr(email_subject, command), " ").length;
// Index of first word after subject and "to"
const recipient_idx = raw_header.length - trimStrByStr(email_subject, " to ").length;
// Used to get the private message-id
const message_id_idx = raw_header.length - trimStrByStr(raw_header, "\r\nmessage-id:<").length;
const message_id = raw_header.slice(message_id_idx).split(">\r\n")[0];
const MAX_MESSAGE_ID_LEN = 128;
const message_id_array = await Uint8ArrayToCharArray(padWithZero(stringToBytes(message_id), MAX_MESSAGE_ID_LEN));
console.log("Indexes into header string are: ", email_from_idx, amount_idx, currency_idx, recipient_idx);
circuitInputs = { circuitInputs = {
in_padded, in_padded,
modulus, modulus,
signature, signature,
in_len_padded_bytes, in_len_padded_bytes,
address, address: address,
address_plus_one, nullifier: nullifier,
body_hash_idx, body_hash_idx,
email_from_idx: email_from_idx.toString(), email_from_idx: email_from_idx.toString(),
command_idx: command_idx.toString(),
message_id_idx: message_id_idx.toString(),
amount_idx: amount_idx.toString(), amount_idx: amount_idx.toString(),
currency_idx: currency_idx.toString(), currency_idx: currency_idx.toString(),
recipient_idx: recipient_idx.toString(), recipient_idx: recipient_idx.toString(),
custom_message_id_from: message_id_array,
custom_message_id_recipient: message_id_array,
}; };
} else { } else {
assert(circuit === CircuitType.SHA, "Invalid circuit type"); assert(circuit === CircuitType.SHA, "Invalid circuit type");
@@ -231,6 +269,8 @@ export async function generate_inputs(raw_email: Buffer | string, eth_address: s
console.log("DKIM verification starting"); console.log("DKIM verification starting");
result = await dkimVerify(email); result = await dkimVerify(email);
console.log("From:", result.headerFrom);
console.log("Results:", result.results[0]);
if (!result.results[0]) { if (!result.results[0]) {
throw new Error(`No result found on dkim output ${result}`); throw new Error(`No result found on dkim output ${result}`);
} else { } else {
@@ -245,7 +285,7 @@ export async function generate_inputs(raw_email: Buffer | string, eth_address: s
const _ = result.results[0].publicKey.toString(); const _ = result.results[0].publicKey.toString();
console.log("DKIM verification successful"); console.log("DKIM verification successful");
// try { // try {
// // TODO: Condiiton code on if there is an internet connection, run this code // // TODO: Condition code on if there is an internet connection, run this code
// var frozen = Cryo.stringify(result); // var frozen = Cryo.stringify(result);
// fs.writeFileSync(`./email_cache_2.json`, frozen, { flag: "w" }); // fs.writeFileSync(`./email_cache_2.json`, frozen, { flag: "w" });
// } catch (e) { // } catch (e) {
@@ -257,7 +297,7 @@ export async function generate_inputs(raw_email: Buffer | string, eth_address: s
let message = result.results[0].status.signature_header; let message = result.results[0].status.signature_header;
let body = result.results[0].body; let body = result.results[0].body;
let body_hash = result.results[0].bodyHash; let body_hash = result.results[0].bodyHash;
let circuitType = CircuitType.EMAIL; let circuitType = CircuitType.EMAIL_SUBJECT;
let pubkey = result.results[0].publicKey; let pubkey = result.results[0].publicKey;
const pubKeyData = pki.publicKeyFromPem(pubkey.toString()); const pubKeyData = pki.publicKeyFromPem(pubkey.toString());
@@ -267,22 +307,6 @@ export async function generate_inputs(raw_email: Buffer | string, eth_address: s
return fin_result.circuitInputs; return fin_result.circuitInputs;
} }
// Only called when the whole function is called from the command line, to read inputs
async function do_generate(writeToFile: boolean = true) {
const { email_file, nonce } = await getArgs();
const email = fs.readFileSync(email_file.trim());
console.log(email);
const gen_inputs = await generate_inputs(email, "0x0000000000000000000000000000000000000000", nonce);
console.log(JSON.stringify(gen_inputs));
if (writeToFile) {
const file_dir = email_file.substring(email_file.lastIndexOf("/") + 1);
const filename = nonce ? `${file_dir}/input_${nonce}.json` : "./circuits/inputs/input.json";
console.log(`Writing to default file ${filename}`);
fs.writeFileSync(filename, JSON.stringify(gen_inputs), { flag: "w" });
}
return gen_inputs;
}
// Sometimes, newline encodings re-encode \r\n as just \n, so re-insert the \r so that the email hashes correctly // Sometimes, newline encodings re-encode \r\n as just \n, so re-insert the \r so that the email hashes correctly
export async function insert13Before10(a: Uint8Array): Promise<Uint8Array> { export async function insert13Before10(a: Uint8Array): Promise<Uint8Array> {
let ret = new Uint8Array(a.length + 1000); let ret = new Uint8Array(a.length + 1000);
@@ -299,7 +323,24 @@ export async function insert13Before10(a: Uint8Array): Promise<Uint8Array> {
return ret.slice(0, j); return ret.slice(0, j);
} }
// Only called when the whole function is called from the command line, to read inputs
// Will generate a test proof with the empty Ethereum address, that cannot be proven by anybody else
async function test_generate(writeToFile: boolean = true) {
const { email_file, nonce } = await getArgs();
const email = fs.readFileSync(email_file.trim());
console.log(email);
const gen_inputs = await generate_inputs(email, "0x0000000000000000000000000000000000000000", nonce);
console.log(JSON.stringify(gen_inputs));
if (writeToFile) {
const file_dir = email_file.substring(0, email_file.lastIndexOf("/") + 1);
const filename = nonce ? `${file_dir}/input_${nonce}.json` : "./circuits/inputs/input.json";
console.log(`Writing to default file ${filename}`);
fs.writeFileSync(filename, JSON.stringify(gen_inputs), { flag: "w" });
}
return gen_inputs;
}
// If file called directly with `npx tsx generate_inputs.ts` // If file called directly with `npx tsx generate_inputs.ts`
if (typeof require !== "undefined" && require.main === module) { if (typeof require !== "undefined" && require.main === module) {
do_generate(true); test_generate(true);
} }

View File

@@ -1,9 +1,8 @@
{ {
"compilerOptions": { "compilerOptions": {
"module": "ESNext", "target": "es2020",
"target": "ESNext", "module": "esnext",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"types": ["vite/client"],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true, "esModuleInterop": true,
@@ -16,8 +15,10 @@
"isolatedModules": true, "isolatedModules": true,
"noEmit": true, "noEmit": true,
"jsx": "react-jsx", "jsx": "react-jsx",
"typeRoots": ["./node_modules/@types", "./types"] "typeRoots": ["./node_modules/@types", "./types"],
"types": ["node", "jest"],
"incremental": true
}, },
"include": [".", "../src/contracts"], "include": ["src", "circuits/scripts/"],
"exclude": ["./dist"] "exclude": ["node_modules"]
} }

File diff suppressed because one or more lines are too long

637
yarn.lock
View File

@@ -2755,7 +2755,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@openzeppelin/contracts@npm:^4.8.3": "@openzeppelin/contracts@npm:^4.9.0":
version: 4.9.1 version: 4.9.1
resolution: "@openzeppelin/contracts@npm:4.9.1" resolution: "@openzeppelin/contracts@npm:4.9.1"
checksum: 9bb3cc6aecd6c56d5ece10d9820d43e6e9c460395b75cc4af9b4e776f1a9e56c9906bc03538b0707e617dc31f1abdfffcbeedfecc8884358d864198ed73f59c1 checksum: 9bb3cc6aecd6c56d5ece10d9820d43e6e9c460395b75cc4af9b4e776f1a9e56c9906bc03538b0707e617dc31f1abdfffcbeedfecc8884358d864198ed73f59c1
@@ -3333,7 +3333,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/jest@npm:*, @types/jest@npm:^29.4.0": "@types/jest@npm:*, @types/jest@npm:^29.5.1":
version: 29.5.2 version: 29.5.2
resolution: "@types/jest@npm:29.5.2" resolution: "@types/jest@npm:29.5.2"
dependencies: dependencies:
@@ -3391,6 +3391,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/mocha@npm:^10.0.1":
version: 10.0.1
resolution: "@types/mocha@npm:10.0.1"
checksum: 224ea9fce7b1734ccdb9aa99a622d902a538ce1847bca7fd22c5fb38adcf3ed536f50f48f587085db988a4bb3c2eb68f4b98e1cd6a38bc5547bd3bbbedc54495
languageName: node
linkType: hard
"@types/ms@npm:*": "@types/ms@npm:*":
version: 0.7.31 version: 0.7.31
resolution: "@types/ms@npm:0.7.31" resolution: "@types/ms@npm:0.7.31"
@@ -4227,6 +4234,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"ansi-colors@npm:4.1.1":
version: 4.1.1
resolution: "ansi-colors@npm:4.1.1"
checksum: 138d04a51076cb085da0a7e2d000c5c0bb09f6e772ed5c65c53cb118d37f6c5f1637506d7155fb5f330f0abcf6f12fa2e489ac3f8cdab9da393bf1bb4f9a32b0
languageName: node
linkType: hard
"ansi-escapes@npm:^4.2.1": "ansi-escapes@npm:^4.2.1":
version: 4.3.2 version: 4.3.2
resolution: "ansi-escapes@npm:4.3.2" resolution: "ansi-escapes@npm:4.3.2"
@@ -4449,6 +4463,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"assertion-error@npm:^1.1.0":
version: 1.1.0
resolution: "assertion-error@npm:1.1.0"
checksum: fd9429d3a3d4fd61782eb3962ae76b6d08aa7383123fca0596020013b3ebd6647891a85b05ce821c47d1471ed1271f00b0545cf6a4326cf2fc91efcc3b0fbecf
languageName: node
linkType: hard
"ast-module-types@npm:^2.7.1": "ast-module-types@npm:^2.7.1":
version: 2.7.1 version: 2.7.1
resolution: "ast-module-types@npm:2.7.1" resolution: "ast-module-types@npm:2.7.1"
@@ -4959,6 +4980,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"browser-stdout@npm:1.3.1":
version: 1.3.1
resolution: "browser-stdout@npm:1.3.1"
checksum: b717b19b25952dd6af483e368f9bcd6b14b87740c3d226c2977a65e84666ffd67000bddea7d911f111a9b6ddc822b234de42d52ab6507bce4119a4cc003ef7b3
languageName: node
linkType: hard
"browserify-aes@npm:^1.0.0, browserify-aes@npm:^1.0.4, browserify-aes@npm:^1.2.0": "browserify-aes@npm:^1.0.0, browserify-aes@npm:^1.0.4, browserify-aes@npm:^1.2.0":
version: 1.2.0 version: 1.2.0
resolution: "browserify-aes@npm:1.2.0" resolution: "browserify-aes@npm:1.2.0"
@@ -5282,7 +5310,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"camelcase@npm:^6.2.0": "camelcase@npm:^6.0.0, camelcase@npm:^6.2.0":
version: 6.3.0 version: 6.3.0
resolution: "camelcase@npm:6.3.0" resolution: "camelcase@npm:6.3.0"
checksum: 8c96818a9076434998511251dcb2761a94817ea17dbdc37f47ac080bd088fc62c7369429a19e2178b993497132c8cbcf5cc1f44ba963e76782ba469c0474938d checksum: 8c96818a9076434998511251dcb2761a94817ea17dbdc37f47ac080bd088fc62c7369429a19e2178b993497132c8cbcf5cc1f44ba963e76782ba469c0474938d
@@ -5317,6 +5345,21 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"chai@npm:^4.3.6, chai@npm:^4.3.7":
version: 4.3.7
resolution: "chai@npm:4.3.7"
dependencies:
assertion-error: ^1.1.0
check-error: ^1.0.2
deep-eql: ^4.1.2
get-func-name: ^2.0.0
loupe: ^2.3.1
pathval: ^1.1.1
type-detect: ^4.0.5
checksum: 0bba7d267848015246a66995f044ce3f0ebc35e530da3cbdf171db744e14cbe301ab913a8d07caf7952b430257ccbb1a4a983c570a7c5748dc537897e5131f7c
languageName: node
linkType: hard
"chalk-template@npm:0.4.0": "chalk-template@npm:0.4.0":
version: 0.4.0 version: 0.4.0
resolution: "chalk-template@npm:0.4.0" resolution: "chalk-template@npm:0.4.0"
@@ -5395,6 +5438,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"check-error@npm:^1.0.2":
version: 1.0.2
resolution: "check-error@npm:1.0.2"
checksum: d9d106504404b8addd1ee3f63f8c0eaa7cd962a1a28eb9c519b1c4a1dc7098be38007fc0060f045ee00f075fbb7a2a4f42abcf61d68323677e11ab98dc16042e
languageName: node
linkType: hard
"check-types@npm:^11.1.1": "check-types@npm:^11.1.1":
version: 11.2.2 version: 11.2.2
resolution: "check-types@npm:11.2.2" resolution: "check-types@npm:11.2.2"
@@ -5431,7 +5481,14 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"chokidar@npm:^3.4.2, chokidar@npm:^3.5.1, chokidar@npm:^3.5.2": "child_process@npm:^1.0.2":
version: 1.0.2
resolution: "child_process@npm:1.0.2"
checksum: bd814d82bc8c6e85ed6fb157878978121cd03b5296c09f6135fa3d081fd9a6a617a6d509c50397711df713af403331241a9c0397a7fad30672051485e156c2a1
languageName: node
linkType: hard
"chokidar@npm:3.5.3, chokidar@npm:^3.4.2, chokidar@npm:^3.5.1, chokidar@npm:^3.5.2":
version: 3.5.3 version: 3.5.3
resolution: "chokidar@npm:3.5.3" resolution: "chokidar@npm:3.5.3"
dependencies: dependencies:
@@ -5499,6 +5556,33 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"circom_runtime@npm:0.1.22":
version: 0.1.22
resolution: "circom_runtime@npm:0.1.22"
dependencies:
ffjavascript: 0.2.57
bin:
calcwit: calcwit.js
checksum: bf7b2e9f74cd7704ebc45ce686d4df49e58ed09114070a98beaab90e5ff7784d1943468ea3315bd4db638e6a76cda24e8b610aa2cd50be56adb4391e20469366
languageName: node
linkType: hard
"circom_tester@npm:^0.0.19":
version: 0.0.19
resolution: "circom_tester@npm:0.0.19"
dependencies:
chai: ^4.3.6
child_process: ^1.0.2
ffjavascript: ^0.2.56
fnv-plus: ^1.3.1
r1csfile: ^0.0.41
snarkjs: 0.5.0
tmp-promise: ^3.0.3
util: ^0.12.4
checksum: 703d7317493ddafb33462b5b0caf9b8a95bde938429e030d024fa7ac41ccb45a9ddbe8ebb93f91138dbee94bb2e0d504570c0aca070802d79ee532ba1ae5db0e
languageName: node
linkType: hard
"circomlibjs@npm:^0.1.2": "circomlibjs@npm:^0.1.2":
version: 0.1.7 version: 0.1.7
resolution: "circomlibjs@npm:0.1.7" resolution: "circomlibjs@npm:0.1.7"
@@ -5595,6 +5679,17 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"cliui@npm:^7.0.2":
version: 7.0.4
resolution: "cliui@npm:7.0.4"
dependencies:
string-width: ^4.2.0
strip-ansi: ^6.0.0
wrap-ansi: ^7.0.0
checksum: ce2e8f578a4813806788ac399b9e866297740eecd4ad1823c27fd344d78b22c5f8597d548adbcc46f0573e43e21e751f39446c5a5e804a12aace402b7a315d7f
languageName: node
linkType: hard
"cliui@npm:^8.0.1": "cliui@npm:^8.0.1":
version: 8.0.1 version: 8.0.1
resolution: "cliui@npm:8.0.1" resolution: "cliui@npm:8.0.1"
@@ -6189,6 +6284,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"decamelize@npm:^4.0.0":
version: 4.0.0
resolution: "decamelize@npm:4.0.0"
checksum: b7d09b82652c39eead4d6678bb578e3bebd848add894b76d0f6b395bc45b2d692fb88d977e7cfb93c4ed6c119b05a1347cef261174916c2e75c0a8ca57da1809
languageName: node
linkType: hard
"decimal.js@npm:^10.4.2": "decimal.js@npm:^10.4.2":
version: 10.4.3 version: 10.4.3
resolution: "decimal.js@npm:10.4.3" resolution: "decimal.js@npm:10.4.3"
@@ -6219,6 +6321,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"deep-eql@npm:^4.1.2":
version: 4.1.3
resolution: "deep-eql@npm:4.1.3"
dependencies:
type-detect: ^4.0.0
checksum: 7f6d30cb41c713973dc07eaadded848b2ab0b835e518a88b91bea72f34e08c4c71d167a722a6f302d3a6108f05afd8e6d7650689a84d5d29ec7fe6220420397f
languageName: node
linkType: hard
"deep-equal@npm:^2.0.5": "deep-equal@npm:^2.0.5":
version: 2.2.1 version: 2.2.1
resolution: "deep-equal@npm:2.2.1" resolution: "deep-equal@npm:2.2.1"
@@ -6581,6 +6692,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"diff@npm:5.0.0":
version: 5.0.0
resolution: "diff@npm:5.0.0"
checksum: f19fe29284b633afdb2725c2a8bb7d25761ea54d321d8e67987ac851c5294be4afeab532bd84531e02583a3fe7f4014aa314a3eda84f5590e7a9e6b371ef3b46
languageName: node
linkType: hard
"diff@npm:^4.0.1": "diff@npm:^4.0.1":
version: 4.0.2 version: 4.0.2
resolution: "diff@npm:4.0.2" resolution: "diff@npm:4.0.2"
@@ -6685,95 +6803,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"double-blind@workspace:.":
version: 0.0.0-use.local
resolution: "double-blind@workspace:."
dependencies:
"@babel/preset-env": ^7.22.2
"@babel/preset-react": ^7.22.0
"@babel/preset-typescript": ^7.21.5
"@esbuild-plugins/node-globals-polyfill": ^0.2.3
"@esbuild-plugins/node-modules-polyfill": ^0.2.2
"@openzeppelin/contracts": ^4.8.3
"@rainbow-me/rainbowkit": ^0.8.0
"@testing-library/jest-dom": ^5.16.3
"@testing-library/react": ^12.1.4
"@testing-library/user-event": ^13.5.0
"@types/atob": ^2.1.2
"@types/jest": ^29.4.0
"@types/lodash": ^4.14.181
"@types/node": ^18.0.6
"@types/node-forge": ^1.3.2
"@types/pako": ^2.0.0
"@types/styled-components": ^5.1.24
"@types/tar-stream": ^2.2.2
"@types/yargs": ^17.0.24
"@vitejs/plugin-react": ^4.0.0
addressparser: ^1.0.1
atob: ^2.1.2
babel-preset-jest: ^29.5.0
base64-sol: ^1.1.0
browserify-fs: ^1.0.0
browserstack-local: ^1.5.1
browserstack-node-sdk: ^1.6.1
buffer: ^6.0.3
circomlibjs: ^0.1.2
cryo: ^0.0.6
crypto-browserify: ^3.12.0
ethereumjs-abi: ^0.6.8
ethers: ^5.7.1
forge-std: ^1.1.2
husky: ^8.0.3
jest: ^29.5.0
jest-environment-jsdom: ^29.5.0
jest-fetch-mock: ^3.0.3
jest-junit: ^15.0.0
jsdom-worker: ^0.3.0
libmime: ^5.1.0
localforage: ^1.10.0
lodash: ^4.17.21
madge: ^6.0.0
msw: ^1.0.1
next: ^12.3.1
node-forge: ^1.3.1
nodemon: ^2.0.19
pako: ^2.1.0
prettier: ^2.7.1
prettier-plugin-solidity: ^1.0.0-beta.24
process: ^0.11.10
puppeteer: 18.1
react: ^17.0.2
react-dom: ^17.0.2
react-dropzone: ^12.0.4
react-error-overlay: 6.0.9
react-json-view: ^1.21.3
react-router: ^6.2.2
react-router-dom: ^6.2.2
react-use: ^17.3.2
readline: ^1.3.0
rollup-plugin-node-polyfills: ^0.2.1
selenium-webdriver: ^4.8.1
serve: ^14.0.1
snarkjs: "https://github.com/sampritipanda/snarkjs.git#fef81fc51d17a734637555c6edbd585ecda02d9e"
sshpk: ^1.17.0
styled-components: ^5.3.5
ts-node: ^10.9.1
ts-node-dev: ^2.0.0
typescript: ^4.8.3
update-browserslist-db: latest
util: ^0.12.5
vite: ^4.3.3
vite-plugin-commonjs: ^0.7.1
vite-plugin-ngmi-polyfill: ^0.0.2
vite-plugin-node-polyfills: ^0.8.2
vite-plugin-svgr: ^2.4.0
vite-tsconfig-paths: ^4.2.0
wagmi: ^0.6.8
web-vitals: ^2.1.4
yargs: ^17.7.1
languageName: unknown
linkType: soft
"duplexer3@npm:^0.1.4": "duplexer3@npm:^0.1.4":
version: 0.1.5 version: 0.1.5
resolution: "duplexer3@npm:0.1.5" resolution: "duplexer3@npm:0.1.5"
@@ -7128,6 +7157,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"escape-string-regexp@npm:4.0.0, escape-string-regexp@npm:^4.0.0":
version: 4.0.0
resolution: "escape-string-regexp@npm:4.0.0"
checksum: 98b48897d93060f2322108bf29db0feba7dd774be96cd069458d1453347b25ce8682ecc39859d4bca2203cc0ab19c237bcc71755eff49a0f8d90beadeeba5cc5
languageName: node
linkType: hard
"escape-string-regexp@npm:^1.0.5": "escape-string-regexp@npm:^1.0.5":
version: 1.0.5 version: 1.0.5
resolution: "escape-string-regexp@npm:1.0.5" resolution: "escape-string-regexp@npm:1.0.5"
@@ -7142,13 +7178,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"escape-string-regexp@npm:^4.0.0":
version: 4.0.0
resolution: "escape-string-regexp@npm:4.0.0"
checksum: 98b48897d93060f2322108bf29db0feba7dd774be96cd069458d1453347b25ce8682ecc39859d4bca2203cc0ab19c237bcc71755eff49a0f8d90beadeeba5cc5
languageName: node
linkType: hard
"escodegen@npm:^2.0.0": "escodegen@npm:^2.0.0":
version: 2.0.0 version: 2.0.0
resolution: "escodegen@npm:2.0.0" resolution: "escodegen@npm:2.0.0"
@@ -7663,7 +7692,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"ffjavascript@npm:^0.2.45, ffjavascript@npm:^0.2.48": "ffjavascript@npm:0.2.57":
version: 0.2.57
resolution: "ffjavascript@npm:0.2.57"
dependencies:
wasmbuilder: 0.0.16
wasmcurves: 0.2.0
web-worker: ^1.2.0
checksum: 8f3e87b3e2739c607dfc2ef8cb36d92202da57b1323987cca09ade378c584a946b9d784607ee5e2193a2de6200afdc254c19f72f79145aa55d9a8688f83f6708
languageName: node
linkType: hard
"ffjavascript@npm:0.2.59, ffjavascript@npm:^0.2.45, ffjavascript@npm:^0.2.48, ffjavascript@npm:^0.2.56":
version: 0.2.59 version: 0.2.59
resolution: "ffjavascript@npm:0.2.59" resolution: "ffjavascript@npm:0.2.59"
dependencies: dependencies:
@@ -7740,6 +7780,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"find-up@npm:5.0.0, find-up@npm:^5.0.0":
version: 5.0.0
resolution: "find-up@npm:5.0.0"
dependencies:
locate-path: ^6.0.0
path-exists: ^4.0.0
checksum: 07955e357348f34660bde7920783204ff5a26ac2cafcaa28bace494027158a97b9f56faaf2d89a6106211a8174db650dd9f503f9c0d526b1202d5554a00b9095
languageName: node
linkType: hard
"find-up@npm:^3.0.0": "find-up@npm:^3.0.0":
version: 3.0.0 version: 3.0.0
resolution: "find-up@npm:3.0.0" resolution: "find-up@npm:3.0.0"
@@ -7759,13 +7809,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"find-up@npm:^5.0.0": "flat@npm:^5.0.2":
version: 5.0.0 version: 5.0.2
resolution: "find-up@npm:5.0.0" resolution: "flat@npm:5.0.2"
dependencies: bin:
locate-path: ^6.0.0 flat: cli.js
path-exists: ^4.0.0 checksum: 12a1536ac746db74881316a181499a78ef953632ddd28050b7a3a43c62ef5462e3357c8c29d76072bb635f147f7a9a1f0c02efef6b4be28f8db62ceb3d5c7f5d
checksum: 07955e357348f34660bde7920783204ff5a26ac2cafcaa28bace494027158a97b9f56faaf2d89a6106211a8174db650dd9f503f9c0d526b1202d5554a00b9095
languageName: node languageName: node
linkType: hard linkType: hard
@@ -7795,6 +7844,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"fnv-plus@npm:^1.3.1":
version: 1.3.1
resolution: "fnv-plus@npm:1.3.1"
checksum: 4d3de8026d538ffab13dfa38ac0662b045b2ad0f920efa54f1ca65f59ad1a49b4d62482c5fcdc9cce0a18d9852df1db97c618937089d85678ce03f2e76b07e8b
languageName: node
linkType: hard
"follow-redirects@npm:^1.14.0": "follow-redirects@npm:^1.14.0":
version: 1.15.2 version: 1.15.2
resolution: "follow-redirects@npm:1.15.2" resolution: "follow-redirects@npm:1.15.2"
@@ -7979,6 +8035,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"get-func-name@npm:^2.0.0":
version: 2.0.0
resolution: "get-func-name@npm:2.0.0"
checksum: 8d82e69f3e7fab9e27c547945dfe5cc0c57fc0adf08ce135dddb01081d75684a03e7a0487466f478872b341d52ac763ae49e660d01ab83741f74932085f693c3
languageName: node
linkType: hard
"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0": "get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0":
version: 1.2.1 version: 1.2.1
resolution: "get-intrinsic@npm:1.2.1" resolution: "get-intrinsic@npm:1.2.1"
@@ -8078,6 +8141,20 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"glob@npm:7.2.0":
version: 7.2.0
resolution: "glob@npm:7.2.0"
dependencies:
fs.realpath: ^1.0.0
inflight: ^1.0.4
inherits: 2
minimatch: ^3.0.4
once: ^1.3.0
path-is-absolute: ^1.0.0
checksum: 78a8ea942331f08ed2e055cb5b9e40fe6f46f579d7fd3d694f3412fe5db23223d29b7fee1575440202e9a7ff9a72ab106a39fee39934c7bedafe5e5f8ae20134
languageName: node
linkType: hard
"glob@npm:^7.0.5, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6": "glob@npm:^7.0.5, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6":
version: 7.2.3 version: 7.2.3
resolution: "glob@npm:7.2.3" resolution: "glob@npm:7.2.3"
@@ -8309,6 +8386,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"he@npm:1.2.0":
version: 1.2.0
resolution: "he@npm:1.2.0"
bin:
he: bin/he
checksum: 3d4d6babccccd79c5c5a3f929a68af33360d6445587d628087f39a965079d84f18ce9c3d3f917ee1e3978916fc833bb8b29377c3b403f919426f91bc6965e7a7
languageName: node
linkType: hard
"headers-polyfill@npm:^3.1.0, headers-polyfill@npm:^3.1.2": "headers-polyfill@npm:^3.1.0, headers-polyfill@npm:^3.1.2":
version: 3.1.2 version: 3.1.2
resolution: "headers-polyfill@npm:3.1.2" resolution: "headers-polyfill@npm:3.1.2"
@@ -8917,6 +9003,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"is-plain-obj@npm:^2.1.0":
version: 2.1.0
resolution: "is-plain-obj@npm:2.1.0"
checksum: cec9100678b0a9fe0248a81743041ed990c2d4c99f893d935545cfbc42876cbe86d207f3b895700c690ad2fa520e568c44afc1605044b535a7820c1d40e38daa
languageName: node
linkType: hard
"is-port-reachable@npm:4.0.0": "is-port-reachable@npm:4.0.0":
version: 4.0.0 version: 4.0.0
resolution: "is-port-reachable@npm:4.0.0" resolution: "is-port-reachable@npm:4.0.0"
@@ -9754,6 +9847,17 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"js-yaml@npm:4.1.0, js-yaml@npm:^4.1.0":
version: 4.1.0
resolution: "js-yaml@npm:4.1.0"
dependencies:
argparse: ^2.0.1
bin:
js-yaml: bin/js-yaml.js
checksum: c7830dfd456c3ef2c6e355cc5a92e6700ceafa1d14bba54497b34a99f0376cecbb3e9ac14d3e5849b426d5a5140709a66237a8c991c675431271c4ce5504151a
languageName: node
linkType: hard
"js-yaml@npm:^3.13.1": "js-yaml@npm:^3.13.1":
version: 3.14.1 version: 3.14.1
resolution: "js-yaml@npm:3.14.1" resolution: "js-yaml@npm:3.14.1"
@@ -9766,17 +9870,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"js-yaml@npm:^4.1.0":
version: 4.1.0
resolution: "js-yaml@npm:4.1.0"
dependencies:
argparse: ^2.0.1
bin:
js-yaml: bin/js-yaml.js
checksum: c7830dfd456c3ef2c6e355cc5a92e6700ceafa1d14bba54497b34a99f0376cecbb3e9ac14d3e5849b426d5a5140709a66237a8c991c675431271c4ce5504151a
languageName: node
linkType: hard
"jsbn@npm:~0.1.0": "jsbn@npm:~0.1.0":
version: 0.1.1 version: 0.1.1
resolution: "jsbn@npm:0.1.1" resolution: "jsbn@npm:0.1.1"
@@ -10250,7 +10343,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"log-symbols@npm:^4.1.0": "log-symbols@npm:4.1.0, log-symbols@npm:^4.1.0":
version: 4.1.0 version: 4.1.0
resolution: "log-symbols@npm:4.1.0" resolution: "log-symbols@npm:4.1.0"
dependencies: dependencies:
@@ -10292,6 +10385,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"loupe@npm:^2.3.1":
version: 2.3.6
resolution: "loupe@npm:2.3.6"
dependencies:
get-func-name: ^2.0.0
checksum: cc83f1b124a1df7384601d72d8d1f5fe95fd7a8185469fec48bb2e4027e45243949e7a013e8d91051a138451ff0552310c32aa9786e60b6a30d1e801bdc2163f
languageName: node
linkType: hard
"lowercase-keys@npm:^1.0.0, lowercase-keys@npm:^1.0.1": "lowercase-keys@npm:^1.0.0, lowercase-keys@npm:^1.0.1":
version: 1.0.1 version: 1.0.1
resolution: "lowercase-keys@npm:1.0.1" resolution: "lowercase-keys@npm:1.0.1"
@@ -10597,6 +10699,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"minimatch@npm:5.0.1":
version: 5.0.1
resolution: "minimatch@npm:5.0.1"
dependencies:
brace-expansion: ^2.0.1
checksum: b34b98463da4754bc526b244d680c69d4d6089451ebe512edaf6dd9eeed0279399cfa3edb19233513b8f830bf4bfcad911dddcdf125e75074100d52f724774f0
languageName: node
linkType: hard
"minimatch@npm:^5.0.1": "minimatch@npm:^5.0.1":
version: 5.1.6 version: 5.1.6
resolution: "minimatch@npm:5.1.6" resolution: "minimatch@npm:5.1.6"
@@ -10725,6 +10836,38 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"mocha@npm:^10.2.0":
version: 10.2.0
resolution: "mocha@npm:10.2.0"
dependencies:
ansi-colors: 4.1.1
browser-stdout: 1.3.1
chokidar: 3.5.3
debug: 4.3.4
diff: 5.0.0
escape-string-regexp: 4.0.0
find-up: 5.0.0
glob: 7.2.0
he: 1.2.0
js-yaml: 4.1.0
log-symbols: 4.1.0
minimatch: 5.0.1
ms: 2.1.3
nanoid: 3.3.3
serialize-javascript: 6.0.0
strip-json-comments: 3.1.1
supports-color: 8.1.1
workerpool: 6.2.1
yargs: 16.2.0
yargs-parser: 20.2.4
yargs-unparser: 2.0.0
bin:
_mocha: bin/_mocha
mocha: bin/mocha.js
checksum: 406c45eab122ffd6ea2003c2f108b2bc35ba036225eee78e0c784b6fa2c7f34e2b13f1dbacef55a4fdf523255d76e4f22d1b5aacda2394bd11666febec17c719
languageName: node
linkType: hard
"module-definition@npm:^3.3.1": "module-definition@npm:^3.3.1":
version: 3.4.0 version: 3.4.0
resolution: "module-definition@npm:3.4.0" resolution: "module-definition@npm:3.4.0"
@@ -10785,7 +10928,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"ms@npm:^2.0.0, ms@npm:^2.1.1": "ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1":
version: 2.1.3 version: 2.1.3
resolution: "ms@npm:2.1.3" resolution: "ms@npm:2.1.3"
checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d
@@ -10859,6 +11002,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"nanoid@npm:3.3.3":
version: 3.3.3
resolution: "nanoid@npm:3.3.3"
bin:
nanoid: bin/nanoid.cjs
checksum: ada019402a07464a694553c61d2dca8a4353645a7d92f2830f0d487fedff403678a0bee5323a46522752b2eab95a0bc3da98b6cccaa7c0c55cd9975130e6d6f0
languageName: node
linkType: hard
"nanoid@npm:^3.3.4, nanoid@npm:^3.3.6": "nanoid@npm:^3.3.4, nanoid@npm:^3.3.6":
version: 3.3.6 version: 3.3.6
resolution: "nanoid@npm:3.3.6" resolution: "nanoid@npm:3.3.6"
@@ -11587,6 +11739,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"pathval@npm:^1.1.1":
version: 1.1.1
resolution: "pathval@npm:1.1.1"
checksum: 090e3147716647fb7fb5b4b8c8e5b55e5d0a6086d085b6cd23f3d3c01fcf0ff56fd3cc22f2f4a033bd2e46ed55d61ed8379e123b42afe7d531a2a5fc8bb556d6
languageName: node
linkType: hard
"pause-stream@npm:0.0.11": "pause-stream@npm:0.0.11":
version: 0.0.11 version: 0.0.11
resolution: "pause-stream@npm:0.0.11" resolution: "pause-stream@npm:0.0.11"
@@ -12177,7 +12336,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"r1csfile@npm:0.0.41": "r1csfile@npm:0.0.41, r1csfile@npm:^0.0.41":
version: 0.0.41 version: 0.0.41
resolution: "r1csfile@npm:0.0.41" resolution: "r1csfile@npm:0.0.41"
dependencies: dependencies:
@@ -12189,6 +12348,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"r1csfile@npm:0.0.45":
version: 0.0.45
resolution: "r1csfile@npm:0.0.45"
dependencies:
"@iden3/bigarray": 0.0.2
"@iden3/binfileutils": 0.0.11
fastfile: 0.0.20
ffjavascript: 0.2.57
checksum: ae2d7ab3f2c37640aea2b4f45753d1f2258f5d904a51d7aaff67c32a931c3e90d22d257e8f10752044fa36b3ac31516f60e503f858913a625015b9b05575ba47
languageName: node
linkType: hard
"randombytes@npm:^2.0.0, randombytes@npm:^2.0.1, randombytes@npm:^2.0.5, randombytes@npm:^2.1.0": "randombytes@npm:^2.0.0, randombytes@npm:^2.0.1, randombytes@npm:^2.0.5, randombytes@npm:^2.1.0":
version: 2.1.0 version: 2.1.0
resolution: "randombytes@npm:2.1.0" resolution: "randombytes@npm:2.1.0"
@@ -13135,6 +13306,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"serialize-javascript@npm:6.0.0":
version: 6.0.0
resolution: "serialize-javascript@npm:6.0.0"
dependencies:
randombytes: ^2.1.0
checksum: 56f90b562a1bdc92e55afb3e657c6397c01a902c588c0fe3d4c490efdcc97dcd2a3074ba12df9e94630f33a5ce5b76a74784a7041294628a6f4306e0ec84bf93
languageName: node
linkType: hard
"serve-handler@npm:6.1.5": "serve-handler@npm:6.1.5":
version: 6.1.5 version: 6.1.5
resolution: "serve-handler@npm:6.1.5" resolution: "serve-handler@npm:6.1.5"
@@ -13292,9 +13472,9 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"snarkjs@https://github.com/sampritipanda/snarkjs.git#fef81fc51d17a734637555c6edbd585ecda02d9e": "snarkjs@npm:0.5.0":
version: 0.5.0 version: 0.5.0
resolution: "snarkjs@https://github.com/sampritipanda/snarkjs.git#commit=fef81fc51d17a734637555c6edbd585ecda02d9e" resolution: "snarkjs@npm:0.5.0"
dependencies: dependencies:
"@iden3/binfileutils": 0.0.11 "@iden3/binfileutils": 0.0.11
bfj: ^7.0.2 bfj: ^7.0.2
@@ -13304,12 +13484,31 @@ __metadata:
fastfile: 0.0.20 fastfile: 0.0.20
ffjavascript: 0.2.56 ffjavascript: 0.2.56
js-sha3: ^0.8.0 js-sha3: ^0.8.0
localforage: ^1.10.0
logplease: ^1.2.15 logplease: ^1.2.15
r1csfile: 0.0.41 r1csfile: 0.0.41
bin: bin:
snarkjs: build/cli.cjs snarkjs: build/cli.cjs
checksum: f2050f0135d50d459ea0edddf3e394e833a2d28c6648e5889b2f896814865e5c60606e978a8a106bd5bfe7e27501c315f249db5b71895d5e7e6e9a87bfcd55ab checksum: f0233103548bcd0f75b2ff8998ba02437e5131486d126c4a6a31355cd3558c7ce4311e21d5f24ea9ca198d0556e91e63e2ec6aef8da44014c16b29b6a7086ca2
languageName: node
linkType: hard
"snarkjs@npm:latest":
version: 0.7.0
resolution: "snarkjs@npm:0.7.0"
dependencies:
"@iden3/binfileutils": 0.0.11
bfj: ^7.0.2
blake2b-wasm: ^2.4.0
circom_runtime: 0.1.22
ejs: ^3.1.6
fastfile: 0.0.20
ffjavascript: 0.2.59
js-sha3: ^0.8.0
logplease: ^1.2.15
r1csfile: 0.0.45
bin:
snarkjs: build/cli.cjs
checksum: 8382c765c9b2fb3fac71807cd58352d0f497793b8eb18843a6ca7d7d6d4c8afbb97becd0e02211c51c0561e97646660a4c3edaed8200d1d182845879156f76b6
languageName: node languageName: node
linkType: hard linkType: hard
@@ -13719,6 +13918,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"strip-json-comments@npm:3.1.1, strip-json-comments@npm:^3.1.1":
version: 3.1.1
resolution: "strip-json-comments@npm:3.1.1"
checksum: 492f73e27268f9b1c122733f28ecb0e7e8d8a531a6662efbd08e22cccb3f9475e90a1b82cab06a392f6afae6d2de636f977e231296400d0ec5304ba70f166443
languageName: node
linkType: hard
"strip-json-comments@npm:^2.0.0, strip-json-comments@npm:~2.0.1": "strip-json-comments@npm:^2.0.0, strip-json-comments@npm:~2.0.1":
version: 2.0.1 version: 2.0.1
resolution: "strip-json-comments@npm:2.0.1" resolution: "strip-json-comments@npm:2.0.1"
@@ -13726,13 +13932,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"strip-json-comments@npm:^3.1.1":
version: 3.1.1
resolution: "strip-json-comments@npm:3.1.1"
checksum: 492f73e27268f9b1c122733f28ecb0e7e8d8a531a6662efbd08e22cccb3f9475e90a1b82cab06a392f6afae6d2de636f977e231296400d0ec5304ba70f166443
languageName: node
linkType: hard
"styled-components@npm:^5.3.5": "styled-components@npm:^5.3.5":
version: 5.3.11 version: 5.3.11
resolution: "styled-components@npm:5.3.11" resolution: "styled-components@npm:5.3.11"
@@ -13802,6 +14001,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"supports-color@npm:8.1.1, supports-color@npm:^8.0.0":
version: 8.1.1
resolution: "supports-color@npm:8.1.1"
dependencies:
has-flag: ^4.0.0
checksum: c052193a7e43c6cdc741eb7f378df605636e01ad434badf7324f17fb60c69a880d8d8fcdcb562cf94c2350e57b937d7425ab5b8326c67c2adc48f7c87c1db406
languageName: node
linkType: hard
"supports-color@npm:^5.3.0, supports-color@npm:^5.5.0": "supports-color@npm:^5.3.0, supports-color@npm:^5.5.0":
version: 5.5.0 version: 5.5.0
resolution: "supports-color@npm:5.5.0" resolution: "supports-color@npm:5.5.0"
@@ -13820,15 +14028,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"supports-color@npm:^8.0.0":
version: 8.1.1
resolution: "supports-color@npm:8.1.1"
dependencies:
has-flag: ^4.0.0
checksum: c052193a7e43c6cdc741eb7f378df605636e01ad434badf7324f17fb60c69a880d8d8fcdcb562cf94c2350e57b937d7425ab5b8326c67c2adc48f7c87c1db406
languageName: node
linkType: hard
"supports-preserve-symlinks-flag@npm:^1.0.0": "supports-preserve-symlinks-flag@npm:^1.0.0":
version: 1.0.0 version: 1.0.0
resolution: "supports-preserve-symlinks-flag@npm:1.0.0" resolution: "supports-preserve-symlinks-flag@npm:1.0.0"
@@ -13953,6 +14152,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"tmp-promise@npm:^3.0.3":
version: 3.0.3
resolution: "tmp-promise@npm:3.0.3"
dependencies:
tmp: ^0.2.0
checksum: f854f5307dcee6455927ec3da9398f139897faf715c5c6dcee6d9471ae85136983ea06662eba2edf2533bdcb0fca66d16648e79e14381e30c7fb20be9c1aa62c
languageName: node
linkType: hard
"tmp@npm:^0.0.33": "tmp@npm:^0.0.33":
version: 0.0.33 version: 0.0.33
resolution: "tmp@npm:0.0.33" resolution: "tmp@npm:0.0.33"
@@ -13962,7 +14170,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"tmp@npm:^0.2.1": "tmp@npm:^0.2.0, tmp@npm:^0.2.1":
version: 0.2.1 version: 0.2.1
resolution: "tmp@npm:0.2.1" resolution: "tmp@npm:0.2.1"
dependencies: dependencies:
@@ -14254,7 +14462,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"type-detect@npm:4.0.8": "type-detect@npm:4.0.8, type-detect@npm:^4.0.0, type-detect@npm:^4.0.5":
version: 4.0.8 version: 4.0.8
resolution: "type-detect@npm:4.0.8" resolution: "type-detect@npm:4.0.8"
checksum: 62b5628bff67c0eb0b66afa371bd73e230399a8d2ad30d852716efcc4656a7516904570cd8631a49a3ce57c10225adf5d0cbdcb47f6b0255fe6557c453925a15 checksum: 62b5628bff67c0eb0b66afa371bd73e230399a8d2ad30d852716efcc4656a7516904570cd8631a49a3ce57c10225adf5d0cbdcb47f6b0255fe6557c453925a15
@@ -15085,6 +15293,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"workerpool@npm:6.2.1":
version: 6.2.1
resolution: "workerpool@npm:6.2.1"
checksum: c2c6eebbc5225f10f758d599a5c016fa04798bcc44e4c1dffb34050cd361d7be2e97891aa44419e7afe647b1f767b1dc0b85a5e046c409d890163f655028b09d
languageName: node
linkType: hard
"wrap-ansi@npm:^5.1.0": "wrap-ansi@npm:^5.1.0":
version: 5.1.0 version: 5.1.0
resolution: "wrap-ansi@npm:5.1.0" resolution: "wrap-ansi@npm:5.1.0"
@@ -15336,6 +15551,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"yargs-parser@npm:20.2.4":
version: 20.2.4
resolution: "yargs-parser@npm:20.2.4"
checksum: d251998a374b2743a20271c2fd752b9fbef24eb881d53a3b99a7caa5e8227fcafd9abf1f345ac5de46435821be25ec12189a11030c12ee6481fef6863ed8b924
languageName: node
linkType: hard
"yargs-parser@npm:^13.1.2": "yargs-parser@npm:^13.1.2":
version: 13.1.2 version: 13.1.2
resolution: "yargs-parser@npm:13.1.2" resolution: "yargs-parser@npm:13.1.2"
@@ -15356,6 +15578,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"yargs-parser@npm:^20.2.2":
version: 20.2.9
resolution: "yargs-parser@npm:20.2.9"
checksum: 8bb69015f2b0ff9e17b2c8e6bfe224ab463dd00ca211eece72a4cd8a906224d2703fb8a326d36fdd0e68701e201b2a60ed7cf81ce0fd9b3799f9fe7745977ae3
languageName: node
linkType: hard
"yargs-parser@npm:^21.1.1": "yargs-parser@npm:^21.1.1":
version: 21.1.1 version: 21.1.1
resolution: "yargs-parser@npm:21.1.1" resolution: "yargs-parser@npm:21.1.1"
@@ -15363,6 +15592,33 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"yargs-unparser@npm:2.0.0":
version: 2.0.0
resolution: "yargs-unparser@npm:2.0.0"
dependencies:
camelcase: ^6.0.0
decamelize: ^4.0.0
flat: ^5.0.2
is-plain-obj: ^2.1.0
checksum: 68f9a542c6927c3768c2f16c28f71b19008710abd6b8f8efbac6dcce26bbb68ab6503bed1d5994bdbc2df9a5c87c161110c1dfe04c6a3fe5c6ad1b0e15d9a8a3
languageName: node
linkType: hard
"yargs@npm:16.2.0":
version: 16.2.0
resolution: "yargs@npm:16.2.0"
dependencies:
cliui: ^7.0.2
escalade: ^3.1.1
get-caller-file: ^2.0.5
require-directory: ^2.1.1
string-width: ^4.2.0
y18n: ^5.0.5
yargs-parser: ^20.2.2
checksum: b14afbb51e3251a204d81937c86a7e9d4bdbf9a2bcee38226c900d00f522969ab675703bee2a6f99f8e20103f608382936034e64d921b74df82b63c07c5e8f59
languageName: node
linkType: hard
"yargs@npm:^13.2.4": "yargs@npm:^13.2.4":
version: 13.3.2 version: 13.3.2
resolution: "yargs@npm:13.3.2" resolution: "yargs@npm:13.3.2"
@@ -15439,6 +15695,99 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"zk-email@workspace:.":
version: 0.0.0-use.local
resolution: "zk-email@workspace:."
dependencies:
"@babel/preset-env": ^7.22.2
"@babel/preset-react": ^7.22.0
"@babel/preset-typescript": ^7.21.5
"@esbuild-plugins/node-globals-polyfill": ^0.2.3
"@esbuild-plugins/node-modules-polyfill": ^0.2.2
"@openzeppelin/contracts": ^4.9.0
"@rainbow-me/rainbowkit": ^0.8.0
"@testing-library/jest-dom": ^5.16.3
"@testing-library/react": ^12.1.4
"@testing-library/user-event": ^13.5.0
"@types/atob": ^2.1.2
"@types/jest": ^29.5.1
"@types/lodash": ^4.14.181
"@types/mocha": ^10.0.1
"@types/node": ^18.0.6
"@types/node-forge": ^1.3.2
"@types/pako": ^2.0.0
"@types/styled-components": ^5.1.24
"@types/tar-stream": ^2.2.2
"@types/yargs": ^17.0.24
"@vitejs/plugin-react": ^4.0.0
addressparser: ^1.0.1
atob: ^2.1.2
babel-preset-jest: ^29.5.0
base64-sol: ^1.1.0
browserify-fs: ^1.0.0
browserstack-local: ^1.5.1
browserstack-node-sdk: ^1.6.1
buffer: ^6.0.3
chai: ^4.3.7
circom_tester: ^0.0.19
circomlibjs: ^0.1.2
cryo: ^0.0.6
crypto-browserify: ^3.12.0
ethereumjs-abi: ^0.6.8
ethers: ^5.7.1
forge-std: ^1.1.2
husky: ^8.0.3
jest: ^29.5.0
jest-environment-jsdom: ^29.5.0
jest-fetch-mock: ^3.0.3
jest-junit: ^15.0.0
jsdom-worker: ^0.3.0
libmime: ^5.1.0
localforage: ^1.10.0
lodash: ^4.17.21
madge: ^6.0.0
mocha: ^10.2.0
msw: ^1.0.1
next: ^12.3.1
node-forge: ^1.3.1
nodemon: ^2.0.19
pako: ^2.1.0
prettier: ^2.7.1
prettier-plugin-solidity: ^1.0.0-beta.24
process: ^0.11.10
puppeteer: 18.1
react: ^17.0.2
react-dom: ^17.0.2
react-dropzone: ^12.0.4
react-error-overlay: 6.0.9
react-json-view: ^1.21.3
react-router: ^6.2.2
react-router-dom: ^6.2.2
react-use: ^17.3.2
readline: ^1.3.0
rollup-plugin-node-polyfills: ^0.2.1
selenium-webdriver: ^4.8.1
serve: ^14.0.1
snarkjs: latest
sshpk: ^1.17.0
styled-components: ^5.3.5
ts-node: ^10.9.1
ts-node-dev: ^2.0.0
typescript: ^4.8.3
update-browserslist-db: latest
util: ^0.12.5
vite: ^4.3.3
vite-plugin-commonjs: ^0.7.1
vite-plugin-ngmi-polyfill: ^0.0.2
vite-plugin-node-polyfills: ^0.8.2
vite-plugin-svgr: ^2.4.0
vite-tsconfig-paths: ^4.2.0
wagmi: ^0.6.8
web-vitals: ^2.1.4
yargs: ^17.7.1
languageName: unknown
linkType: soft
"zustand@npm:^4.0.0": "zustand@npm:^4.0.0":
version: 4.3.8 version: 4.3.8
resolution: "zustand@npm:4.3.8" resolution: "zustand@npm:4.3.8"