├── .gitmodules ├── circuit ├── .circuit-root ├── templates │ ├── .gitignore │ ├── update_ctags.sh │ ├── helpers │ │ ├── hashtofield │ │ │ ├── PoseidonBN254Hash.circom │ │ │ ├── Hash64BitLimbsToFieldWithLen.circom │ │ │ ├── README.md │ │ │ └── HashBytesToFieldWithLen.circom │ │ ├── base64url │ │ │ ├── README.md │ │ │ ├── Base64UrlDecode.circom │ │ │ └── Base64UrlLookup.circom │ │ ├── packing │ │ │ ├── BigEndianBits2Num.circom │ │ │ ├── AssertIsBytes.circom │ │ │ ├── AssertIs64BitLimbs.circom │ │ │ ├── Bytes2BigEndianBits.circom │ │ │ ├── ChunksToFieldElem.circom │ │ │ ├── Num2BigEndianBits.circom │ │ │ ├── ChunksToFieldElems.circom │ │ │ └── BigEndianBitsToScalars.circom │ │ ├── arrays │ │ │ ├── SelectArrayValue.circom │ │ │ ├── ArraySelectorComplex.circom │ │ │ ├── SingleOneArray.circom │ │ │ ├── RightArraySelector.circom │ │ │ ├── LeftArraySelector.circom │ │ │ └── SingleNegOneArray.circom │ │ ├── jwt │ │ │ ├── EnforceNotNested.circom │ │ │ ├── BracketsMap.circom │ │ │ ├── StringBodies.circom │ │ │ └── EmailVerifiedCheck.circom │ │ ├── strings │ │ │ ├── IsWhitespace.circom │ │ │ ├── AssertIsAsciiDigits.circom │ │ │ ├── AsciiDigitsToScalar.circom │ │ │ └── AssertIsConcatenation.circom │ │ ├── bigint │ │ │ ├── CheckCarryToZero.circom │ │ │ └── BigLessThan.circom │ │ ├── rsa │ │ │ ├── FpPow65537Mod.circom │ │ │ ├── RSA_PKCS1_v1_5_Verify.circom │ │ │ └── FpMul.circom │ │ └── sha │ │ │ └── SHA2_256_PaddingVerify.circom │ ├── stdlib │ │ ├── circuits │ │ │ ├── ConditionallyAssertEqual.circom │ │ │ ├── InvertBinaryArray.circom │ │ │ ├── ElementwiseMul.circom │ │ │ └── Sum.circom │ │ └── functions │ │ │ ├── assert_bits_fit_scalar.circom │ │ │ ├── min_num_bits.circom │ │ │ ├── MAX_BITS.circom │ │ │ └── log2_floor.circom │ └── main.circom ├── .gitignore ├── tests │ ├── rsa_verify_test.circom │ ├── sha2_padding_verify_test.circom │ ├── jwt_field_parsing │ │ ├── parse_quoted_test.circom │ │ ├── parse_unquoted_test.circom │ │ ├── parse_email_verified_field_test.circom │ │ └── email_verified_check_test.circom │ ├── misc │ │ ├── sum_test.circom │ │ ├── assert_equal_if_true_test.circom │ │ ├── is_whitespace_test.circom │ │ ├── string_bodies_test.circom │ │ ├── brackets_map_test.circom │ │ └── brackets_depth_map_test.circom │ ├── arrays │ │ ├── assert_is_ascii_digits_test.circom │ │ ├── assert_is_ascii_digits_large_test.circom │ │ ├── assert_is_ascii_digits_max_len_test.circom │ │ ├── single_one_array_test.circom │ │ ├── single_one_array_large_test.circom │ │ ├── single_one_array_small_test.circom │ │ ├── left_array_selector_test.circom │ │ ├── single_neg_one_array_test.circom │ │ ├── left_array_selector_large_test.circom │ │ ├── left_array_selector_small_test.circom │ │ ├── right_array_selector_test.circom │ │ ├── single_neg_one_array_small_test.circom │ │ ├── right_array_selector_large_test.circom │ │ ├── right_array_selector_small_test.circom │ │ ├── single_neg_one_array_large_test.circom │ │ ├── select_array_value_test.circom │ │ ├── array_selector_test.circom │ │ ├── ascii_digits_to_scalar_test.circom │ │ ├── select_array_value_small_test.circom │ │ ├── array_selector_test_small.circom │ │ ├── ascii_digits_to_scalar_small_test.circom │ │ ├── select_array_value_large_test.circom │ │ ├── array_selector_test_large.circom │ │ ├── ascii_digits_to_scalar_large_test.circom │ │ ├── array_selector_complex_small_test.circom │ │ ├── elementwise_mul_test.circom │ │ ├── array_selector_complex_large_test.circom │ │ ├── assert_is_substring_test.circom │ │ ├── assert_is_substring_large_test.circom │ │ ├── assert_is_substring_no_padding_test.circom │ │ ├── array_selector_complex_test.circom │ │ ├── invert_binary_array_test.circom │ │ ├── is_substring_test.circom │ │ ├── is_substring_large_test.circom │ │ ├── is_substring_no_padding_test.circom │ │ ├── is_substring_small_test.circom │ │ ├── assert_is_concatenation_test.circom │ │ ├── assert_is_concatenation_small_test.circom │ │ └── assert_is_concatenation_large_test.circom │ ├── base64_decoded_length.circom │ ├── base64_lookup_test.circom │ ├── packing │ │ ├── num2bits_be_test.circom │ │ ├── bytes_to_big_endian_bits_test.circom │ │ ├── bits2num_big_endian_test.circom │ │ └── big_endian_bits_to_scalars_test.circom │ ├── bigint │ │ └── big_less_than_test.circom │ ├── sha_test.circom │ ├── array_selector_test.circom │ ├── base64_decode_test_short.circom │ ├── base64_decode_test.circom │ └── assert_is_substring_test.circom ├── benches │ └── strings │ │ ├── SHA2_256_Compression_Bench.circom │ │ ├── AssertIsConcatenation_Bench.circom │ │ ├── SHA2_256.js │ │ └── AssertIsConcatenation.js ├── package.json ├── Cargo.toml ├── tools │ └── test_rsa_privkey.pem └── README.md ├── rust-rapidsnark ├── rapidsnark │ ├── src │ │ ├── scalar.cpp │ │ ├── misc.cpp │ │ ├── splitparstr.hpp │ │ ├── naf.hpp │ │ ├── scalar.hpp │ │ ├── logging.hpp │ │ ├── test.txt │ │ ├── misc.hpp │ │ ├── fq_element.hpp │ │ ├── fr_element.hpp │ │ ├── exp2.hpp │ │ ├── random_generator.hpp │ │ ├── spinlock.hpp │ │ ├── exp.hpp │ │ ├── wtns_utils.hpp │ │ ├── alt_bn128.cpp │ │ ├── fullprover.hpp │ │ ├── splitparstr.cpp │ │ ├── fileloader.hpp │ │ ├── fft.hpp │ │ ├── f2field.hpp │ │ ├── multiexp.hpp │ │ ├── alt_bn128.hpp │ │ ├── naf.cpp │ │ ├── zkey_utils.hpp │ │ ├── splitparstr_test.cpp │ │ └── pointparallelprocessor.hpp │ ├── native-env.ini │ ├── build_lib.sh │ ├── subprojects │ │ ├── oneTBB.wrap │ │ ├── scope_guard.wrap │ │ └── json.wrap │ ├── README.md │ ├── .vscode │ │ ├── launch.json │ │ ├── tasks.json │ │ └── settings.json │ └── Makefile ├── wrapper.hpp ├── README.md └── Cargo.toml ├── license_header.txt ├── prover-service ├── depot.json ├── resources │ ├── toy_circuit │ │ ├── toy_input.json │ │ ├── README.md │ │ ├── toy.r1cs │ │ ├── toy.wtns │ │ ├── toy_1.zkey │ │ └── toy.circom │ └── README.md ├── private_key_for_testing.txt ├── private_key_for_testing_another.txt ├── src │ ├── external_resources │ │ └── mod.rs │ ├── input_processing │ │ └── mod.rs │ ├── request_handler │ │ ├── mod.rs │ │ └── deployment_information.rs │ ├── tests │ │ ├── mod.rs │ │ ├── utils.rs │ │ └── training_wheels.rs │ ├── lib.rs │ └── error.rs ├── config.yml ├── test_jwk.json ├── config_local_testing.yml ├── circuit_config.yml ├── Dockerfile └── Cargo.toml ├── .dockerignore ├── keyless-common ├── src │ ├── lib.rs │ ├── input_processing │ │ ├── mod.rs │ │ ├── encoding.rs │ │ └── circuit_config.rs │ └── types.rs └── Cargo.toml ├── rust-toolchain.toml ├── .cargo └── config.toml ├── .gitignore ├── release-helper └── Cargo.toml ├── scripts ├── run_prover_service.sh ├── python │ ├── prover_service.py │ ├── misc.py │ ├── main.py │ └── setups │ │ ├── ceremony_setup.py │ │ └── gh_release.py ├── rust_lint.sh └── task.sh ├── vk-diff ├── Cargo.toml └── README.md ├── .pre-commit-config.yaml ├── .github ├── workflows │ ├── run-tests.yaml │ └── build-image.yaml └── PULL_REQUEST_TEMPLATE.md ├── git-hooks └── compile-circom-if-needed-pre-commit └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /circuit/.circuit-root: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/scalar.cpp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /circuit/templates/.gitignore: -------------------------------------------------------------------------------- 1 | tags 2 | *.swp 3 | -------------------------------------------------------------------------------- /license_header.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) Aptos Foundation 2 | -------------------------------------------------------------------------------- /prover-service/depot.json: -------------------------------------------------------------------------------- 1 | {"id":"jnx6z4pq5q"} 2 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/misc.cpp: -------------------------------------------------------------------------------- 1 | #include "misc.hpp" 2 | -------------------------------------------------------------------------------- /prover-service/resources/toy_circuit/toy_input.json: -------------------------------------------------------------------------------- 1 | { "a" : 2, "b": 3 } 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | /target 2 | formatted_input.json 3 | .github/ 4 | .idea/ 5 | .git/ 6 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/native-env.ini: -------------------------------------------------------------------------------- 1 | [binaries] 2 | c = 'clang' 3 | cpp = 'clang++' 4 | -------------------------------------------------------------------------------- /prover-service/private_key_for_testing.txt: -------------------------------------------------------------------------------- 1 | 0x8c75cacb54de1af0bd7b6c0549548b8d39a8177320f46ca5f767e5ed603dc08b -------------------------------------------------------------------------------- /rust-rapidsnark/wrapper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define USE_OPENMP 4 | 5 | #include 6 | -------------------------------------------------------------------------------- /circuit/templates/update_ctags.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | find . -name "*.circom" | ctags --language-force=C -L- 4 | -------------------------------------------------------------------------------- /prover-service/private_key_for_testing_another.txt: -------------------------------------------------------------------------------- 1 | 0x6bf4bd737ac8cc87841b5e83d396ff4e9197b014b7b395434d09c5828cca0e1d -------------------------------------------------------------------------------- /prover-service/resources/README.md: -------------------------------------------------------------------------------- 1 | `202405_vk.vkey` is used for a vk load from file test at `prover/src/groth16_vk.rs` 2 | -------------------------------------------------------------------------------- /prover-service/resources/toy_circuit/README.md: -------------------------------------------------------------------------------- 1 | This toy circuit is used for a load bearing test at `prover/src/tests/smoke.rs` 2 | -------------------------------------------------------------------------------- /keyless-common/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Aptos Foundation 2 | 3 | pub mod input_processing; 4 | pub mod snark_js_groth16; 5 | pub mod types; 6 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.89.0" 3 | 4 | components = ["cargo", "clippy", "rustc", "rustfmt", "rust-docs", "rust-std"] 5 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/splitparstr.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | std::vector splitParStr(std::string s); 5 | -------------------------------------------------------------------------------- /prover-service/resources/toy_circuit/toy.r1cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aptos-labs/keyless-zk-proofs/HEAD/prover-service/resources/toy_circuit/toy.r1cs -------------------------------------------------------------------------------- /prover-service/resources/toy_circuit/toy.wtns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aptos-labs/keyless-zk-proofs/HEAD/prover-service/resources/toy_circuit/toy.wtns -------------------------------------------------------------------------------- /prover-service/resources/toy_circuit/toy_1.zkey: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aptos-labs/keyless-zk-proofs/HEAD/prover-service/resources/toy_circuit/toy_1.zkey -------------------------------------------------------------------------------- /circuit/.gitignore: -------------------------------------------------------------------------------- 1 | aptos-keyless-trusted-setup-contributions-may-2024 2 | node_modules/ 3 | templates/main.r1cs 4 | templates/main.sym 5 | templates/main_js 6 | -------------------------------------------------------------------------------- /prover-service/src/external_resources/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Aptos Foundation 2 | 3 | pub mod jwk_fetcher; 4 | pub mod jwk_types; 5 | pub mod prover_config; 6 | -------------------------------------------------------------------------------- /circuit/tests/rsa_verify_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/rsa/RSA_PKCS1_v1_5_Verify.circom"; 4 | 5 | component main = RSA_PKCS1_v1_5_Verify(64, 32); 6 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/build_lib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | rm -rf build 6 | meson setup --native-file=native-env.ini build 7 | cd build 8 | meson compile 9 | 10 | -------------------------------------------------------------------------------- /circuit/benches/strings/SHA2_256_Compression_Bench.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "circomlib/circuits/sha256/sha256compression.circom"; 4 | 5 | component main = Sha256compression(); -------------------------------------------------------------------------------- /circuit/tests/sha2_padding_verify_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/sha/SHA2_256_PaddingVerify.circom"; 4 | 5 | component main = SHA2_256_PaddingVerify(256); // 4 blocks 6 | -------------------------------------------------------------------------------- /prover-service/src/input_processing/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Aptos Foundation 2 | 3 | pub mod field_check_input; 4 | pub mod field_parser; 5 | pub mod input_signals; 6 | pub mod public_inputs_hash; 7 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/naf.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FFIASM_NAF_HPP 2 | #define FFIASM_NAF_HPP 3 | #include 4 | 5 | void buildNaf(uint8_t *r, uint8_t* scalar, unsigned int scalarSize); 6 | 7 | #endif 8 | 9 | -------------------------------------------------------------------------------- /circuit/tests/jwt_field_parsing/parse_quoted_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/jwt/ParseJWTFieldWithQuotedValue.circom"; 4 | 5 | 6 | component main = ParseJWTFieldWithQuotedValue(60, 30, 30); 7 | -------------------------------------------------------------------------------- /keyless-common/src/input_processing/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Aptos Foundation 2 | 3 | pub mod bits; 4 | pub mod circuit_config; 5 | pub mod circuit_input_signals; 6 | pub mod encoding; 7 | pub mod jwt; 8 | pub mod sha; 9 | -------------------------------------------------------------------------------- /prover-service/config.yml: -------------------------------------------------------------------------------- 1 | resources_dir: "/resources/ceremonies" 2 | jwk_issuers: 3 | - issuer_name: "https://accounts.google.com" 4 | issuer_jwk_url: "https://www.googleapis.com/oauth2/v3/certs" 5 | port: 8080 6 | -------------------------------------------------------------------------------- /circuit/tests/jwt_field_parsing/parse_unquoted_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/jwt/ParseJWTFieldWithUnquotedValue.circom"; 4 | 5 | 6 | component main = ParseJWTFieldWithUnquotedValue(60, 30, 30); 7 | -------------------------------------------------------------------------------- /circuit/tests/jwt_field_parsing/parse_email_verified_field_test.circom: -------------------------------------------------------------------------------- 1 | 2 | pragma circom 2.2.2; 3 | 4 | include "helpers/jwt/ParseEmailVerifiedField.circom"; 5 | 6 | 7 | component main = ParseEmailVerifiedField(30, 20, 10); 8 | -------------------------------------------------------------------------------- /rust-rapidsnark/README.md: -------------------------------------------------------------------------------- 1 | This crate is a rust wrapper for the C++ rapidsnark library. It is used by 2 | the `prover-service` crate to compute proofs. See 3 | `../prover-service/README.md` for how to run/test the prover service. 4 | -------------------------------------------------------------------------------- /prover-service/src/request_handler/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Aptos Foundation 2 | 3 | pub mod deployment_information; 4 | pub mod handler; 5 | pub mod prover_handler; 6 | pub mod prover_state; 7 | pub mod training_wheels; 8 | pub mod types; 9 | -------------------------------------------------------------------------------- /prover-service/src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Aptos Foundation 2 | pub mod federated_jwk; 3 | pub mod jwk_fetcher; 4 | pub mod prover_handler; 5 | pub mod request_handler; 6 | pub mod training_wheels; 7 | pub mod types; 8 | pub mod utils; 9 | -------------------------------------------------------------------------------- /prover-service/resources/toy_circuit/toy.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | template Main() { 4 | signal input a; 5 | signal input b; 6 | 7 | signal c <== a * b; 8 | 9 | c === 6; 10 | } 11 | 12 | component main { public [ a ] } = Main(); 13 | -------------------------------------------------------------------------------- /circuit/templates/helpers/hashtofield/PoseidonBN254Hash.circom: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: Alin Tomescu 3 | */ 4 | pragma circom 2.2.2; 5 | 6 | /** 7 | * A bus to denote a PoseidonBN254 hash in a type-safe way. 8 | */ 9 | bus PoseidonBN254Hash() { 10 | signal value; 11 | } -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/scalar.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class Scalar { 4 | unsigned int allocSize; 5 | unsigned int size; 6 | uint8_t *number; 7 | public: 8 | 9 | Scalar(unsigned int initialSize); 10 | setNumber(u8 ) 11 | 12 | 13 | }; -------------------------------------------------------------------------------- /prover-service/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Aptos Foundation 2 | 3 | extern crate core; 4 | 5 | pub mod error; 6 | pub mod external_resources; 7 | pub mod input_processing; 8 | pub mod metrics; 9 | pub mod request_handler; 10 | pub mod utils; 11 | 12 | #[cfg(test)] 13 | pub mod tests; 14 | -------------------------------------------------------------------------------- /circuit/templates/stdlib/circuits/ConditionallyAssertEqual.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | // Asserts that `in[0] === in[1]` only when `bool` is 1. 4 | template ConditionallyAssertEqual() { 5 | signal input in[2]; 6 | signal input {binary} bool; 7 | 8 | (in[0]-in[1]) * bool === 0; 9 | } -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/subprojects/oneTBB.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory = oneTBB-2022.0.0 3 | source_url = https://github.com/uxlfoundation/oneTBB/archive/refs/tags/v2022.0.0.tar.gz 4 | source_filename = v2022.0.0.tar.gz 5 | source_hash = e8e89c9c345415b17b30a2db3095ba9d47647611662073f7fbf54ad48b7f3c2a 6 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/subprojects/scope_guard.wrap: -------------------------------------------------------------------------------- 1 | 2 | [wrap-file] 3 | directory = scope_guard-0.9.1 4 | source_url = https://github.com/Neargye/scope_guard/archive/refs/tags/v0.9.1.tar.gz 5 | source_filename = v0.9.1.tar.gz 6 | source_hash = c0a8e465d4ac239b602a24713d83fb315769145ae59312c8329f4ee39477cc05 7 | -------------------------------------------------------------------------------- /circuit/tests/misc/sum_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "stdlib/circuits/Sum.circom"; 4 | 5 | template sum_test() { 6 | var len = 10; 7 | signal input nums[len]; 8 | signal input sum; 9 | var result = Sum(len)(nums); 10 | 11 | sum === result; 12 | } 13 | 14 | component main = sum_test( 15 | ); 16 | -------------------------------------------------------------------------------- /circuit/benches/strings/AssertIsConcatenation_Bench.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/AssertIsConcatenation.circom"; 4 | 5 | component main = AssertIsConcatenation( 6 | 192*8, // MAX_B64U_JWT_NO_SIG_LEN 7 | 300, // MAX_B64U_JWT_HEADER_W_DOT_LEN 8 | 192*8-64 // MAX_B64U_JWT_PAYLOAD_SHA2_PADDED_LEN 9 | ); -------------------------------------------------------------------------------- /circuit/tests/misc/assert_equal_if_true_test.circom: -------------------------------------------------------------------------------- 1 | 2 | pragma circom 2.2.2; 3 | 4 | include "stdlib/circuits/ConditionallyAssertEqual.circom"; 5 | 6 | template Test() { 7 | signal input in[2]; 8 | signal input bool; 9 | 10 | signal {binary} bool_tagged <== bool; 11 | 12 | ConditionallyAssertEqual()(in, bool_tagged); 13 | } 14 | 15 | component main = Test(); 16 | -------------------------------------------------------------------------------- /circuit/tests/arrays/assert_is_ascii_digits_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/AssertIsAsciiDigits.circom"; 4 | 5 | template ascii_digits_test(maxNumDigits) { 6 | signal input in[maxNumDigits]; 7 | signal input len; 8 | 9 | AssertIsAsciiDigits(maxNumDigits)(in, len); 10 | } 11 | 12 | component main = ascii_digits_test( 13 | 20 14 | ); 15 | -------------------------------------------------------------------------------- /circuit/tests/arrays/assert_is_ascii_digits_large_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/AssertIsAsciiDigits.circom"; 4 | 5 | template ascii_digits_test(maxNumDigits) { 6 | signal input in[maxNumDigits]; 7 | signal input len; 8 | 9 | AssertIsAsciiDigits(maxNumDigits)(in, len); 10 | } 11 | 12 | component main = ascii_digits_test( 13 | 2000 14 | ); 15 | -------------------------------------------------------------------------------- /circuit/tests/arrays/assert_is_ascii_digits_max_len_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/AssertIsAsciiDigits.circom"; 4 | 5 | template ascii_digits_test(maxNumDigits) { 6 | signal input in[maxNumDigits]; 7 | signal input len; 8 | 9 | AssertIsAsciiDigits(maxNumDigits)(in, len); 10 | } 11 | 12 | component main = ascii_digits_test( 13 | 8 14 | ); 15 | -------------------------------------------------------------------------------- /circuit/tests/arrays/single_one_array_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/SingleOneArray.circom"; 4 | 5 | template single_one_array_test(len) { 6 | signal input index; 7 | signal input expected_output[len]; 8 | 9 | signal out[len] <== SingleOneArray(len)(index); 10 | out === expected_output; 11 | } 12 | 13 | component main = single_one_array_test( 14 | 8 15 | ); 16 | -------------------------------------------------------------------------------- /circuit/tests/arrays/single_one_array_large_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/SingleOneArray.circom"; 4 | 5 | template single_one_array_test(len) { 6 | signal input index; 7 | signal input expected_output[len]; 8 | 9 | signal out[len] <== SingleOneArray(len)(index); 10 | out === expected_output; 11 | } 12 | 13 | component main = single_one_array_test( 14 | 2000 15 | ); 16 | -------------------------------------------------------------------------------- /circuit/tests/arrays/single_one_array_small_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/SingleOneArray.circom"; 4 | 5 | template single_one_array_test(len) { 6 | signal input index; 7 | signal input expected_output[len]; 8 | 9 | signal out[len] <== SingleOneArray(len)(index); 10 | out === expected_output; 11 | } 12 | 13 | component main = single_one_array_test( 14 | 1 15 | ); 16 | -------------------------------------------------------------------------------- /circuit/tests/misc/is_whitespace_test.circom: -------------------------------------------------------------------------------- 1 | 2 | pragma circom 2.2.2; 3 | 4 | include "helpers/strings/IsWhitespace.circom"; 5 | 6 | template is_whitespace_test() { 7 | signal input char; 8 | signal input result; 9 | component is_whitespace = IsWhitespace(); 10 | is_whitespace.char <== char; 11 | is_whitespace.is_whitespace === result; 12 | 13 | } 14 | 15 | component main = is_whitespace_test( 16 | ); 17 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | xclippy = [ 3 | "clippy", 4 | "--workspace", 5 | "--all-targets", 6 | "--", 7 | "-Dwarnings", 8 | "-Wclippy::all", 9 | "-Aclippy::upper_case_acronyms", 10 | "-Aclippy::enum-variant-names", 11 | "-Aclippy::result-large-err", 12 | "-Aclippy::mutable-key-type", 13 | "-Wclippy::needless-borrow", 14 | ] 15 | 16 | [build] 17 | rustflags = ["--cfg", "tokio_unstable"] 18 | -------------------------------------------------------------------------------- /circuit/tests/arrays/left_array_selector_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/LeftArraySelector.circom"; 4 | 5 | template left_array_selector_test(len) { 6 | signal input index; 7 | signal input expected_output[len]; 8 | 9 | signal out[len] <== LeftArraySelector(len)(index); 10 | out === expected_output; 11 | } 12 | 13 | component main = left_array_selector_test( 14 | 8 15 | ); 16 | -------------------------------------------------------------------------------- /circuit/tests/arrays/single_neg_one_array_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/SingleNegOneArray.circom"; 4 | 5 | template single_neg_one_array_test(len) { 6 | signal input index; 7 | signal input expected_output[len]; 8 | 9 | signal out[len] <== SingleNegOneArray(len)(index); 10 | out === expected_output; 11 | } 12 | 13 | component main = single_neg_one_array_test( 14 | 8 15 | ); 16 | -------------------------------------------------------------------------------- /circuit/tests/arrays/left_array_selector_large_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/LeftArraySelector.circom"; 4 | 5 | template left_array_selector_test(len) { 6 | signal input index; 7 | signal input expected_output[len]; 8 | 9 | signal out[len] <== LeftArraySelector(len)(index); 10 | out === expected_output; 11 | } 12 | 13 | component main = left_array_selector_test( 14 | 2000 15 | ); 16 | -------------------------------------------------------------------------------- /circuit/tests/arrays/left_array_selector_small_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/LeftArraySelector.circom"; 4 | 5 | template left_array_selector_test(len) { 6 | signal input index; 7 | signal input expected_output[len]; 8 | 9 | signal out[len] <== LeftArraySelector(len)(index); 10 | out === expected_output; 11 | } 12 | 13 | component main = left_array_selector_test( 14 | 1 15 | ); 16 | -------------------------------------------------------------------------------- /circuit/tests/arrays/right_array_selector_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/RightArraySelector.circom"; 4 | 5 | template right_array_selector_test(len) { 6 | signal input index; 7 | signal input expected_output[len]; 8 | 9 | signal out[len] <== RightArraySelector(len)(index); 10 | out === expected_output; 11 | } 12 | 13 | component main = right_array_selector_test( 14 | 8 15 | ); 16 | -------------------------------------------------------------------------------- /circuit/tests/arrays/single_neg_one_array_small_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/SingleNegOneArray.circom"; 4 | 5 | template single_neg_one_array_test(len) { 6 | signal input index; 7 | signal input expected_output[len]; 8 | 9 | signal out[len] <== SingleNegOneArray(len)(index); 10 | out === expected_output; 11 | } 12 | 13 | component main = single_neg_one_array_test( 14 | 1 15 | ); 16 | -------------------------------------------------------------------------------- /circuit/tests/base64_decoded_length.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/base64url/Base64UrlDecodedLength.circom"; 4 | 5 | template base64url_decoded_length_test() { 6 | signal input encoded_len; 7 | signal input expected_decoded_len; 8 | 9 | signal result <== Base64UrlDecodedLength(512)(encoded_len); 10 | result === expected_decoded_len; 11 | } 12 | 13 | component main = base64url_decoded_length_test(); 14 | -------------------------------------------------------------------------------- /circuit/tests/arrays/right_array_selector_large_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/RightArraySelector.circom"; 4 | 5 | template right_array_selector_test(len) { 6 | signal input index; 7 | signal input expected_output[len]; 8 | 9 | signal out[len] <== RightArraySelector(len)(index); 10 | out === expected_output; 11 | } 12 | 13 | component main = right_array_selector_test( 14 | 2000 15 | ); 16 | -------------------------------------------------------------------------------- /circuit/tests/arrays/right_array_selector_small_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/RightArraySelector.circom"; 4 | 5 | template right_array_selector_test(len) { 6 | signal input index; 7 | signal input expected_output[len]; 8 | 9 | signal out[len] <== RightArraySelector(len)(index); 10 | out === expected_output; 11 | } 12 | 13 | component main = right_array_selector_test( 14 | 1 15 | ); 16 | -------------------------------------------------------------------------------- /circuit/tests/arrays/single_neg_one_array_large_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/SingleNegOneArray.circom"; 4 | 5 | template single_neg_one_array_test(len) { 6 | signal input index; 7 | signal input expected_output[len]; 8 | 9 | signal out[len] <== SingleNegOneArray(len)(index); 10 | out === expected_output; 11 | } 12 | 13 | component main = single_neg_one_array_test( 14 | 2000 15 | ); 16 | -------------------------------------------------------------------------------- /circuit/tests/base64_lookup_test.circom: -------------------------------------------------------------------------------- 1 | 2 | pragma circom 2.2.2; 3 | 4 | include "helpers/base64url/Base64UrlLookup.circom"; 5 | 6 | template base64url_lookup_test() { 7 | signal input in_b64_char; 8 | signal input out_num; 9 | component base64url_lookup = Base64UrlLookup(); 10 | base64url_lookup.in <== in_b64_char; 11 | out_num === base64url_lookup.out; 12 | 13 | } 14 | 15 | component main = base64url_lookup_test(); 16 | -------------------------------------------------------------------------------- /circuit/tests/misc/string_bodies_test.circom: -------------------------------------------------------------------------------- 1 | 2 | 3 | pragma circom 2.2.2; 4 | 5 | include "helpers/jwt/StringBodies.circom"; 6 | 7 | template string_bodies_test() { 8 | var len = 13; 9 | signal input in[len]; 10 | signal input out[len]; 11 | component string_bodies = StringBodies(len); 12 | string_bodies.in <== in; 13 | string_bodies.out === out; 14 | 15 | } 16 | 17 | component main = string_bodies_test( 18 | ); 19 | -------------------------------------------------------------------------------- /circuit/tests/arrays/select_array_value_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/SelectArrayValue.circom"; 4 | 5 | template select_array_value_test(len) { 6 | signal input array[len]; 7 | signal input index; 8 | signal input expected_output; 9 | 10 | signal out <== SelectArrayValue(len)(array, index); 11 | out === expected_output; 12 | } 13 | 14 | component main = select_array_value_test( 15 | 8 16 | ); 17 | -------------------------------------------------------------------------------- /circuit/tests/arrays/array_selector_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/ArraySelector.circom"; 4 | 5 | template array_selector_test(len) { 6 | signal input start_index; 7 | signal input end_index; 8 | signal input expected_output[len]; 9 | 10 | signal out[len] <== ArraySelector(len)(start_index, end_index); 11 | out === expected_output; 12 | } 13 | 14 | component main = array_selector_test( 15 | 8 16 | ); 17 | -------------------------------------------------------------------------------- /circuit/tests/arrays/ascii_digits_to_scalar_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/AsciiDigitsToScalar.circom"; 4 | 5 | template ascii_digits_test(maxLen) { 6 | signal input digits[maxLen]; 7 | signal input len; 8 | signal input expected_output; 9 | 10 | signal out <== AsciiDigitsToScalar(maxLen)(digits, len); 11 | expected_output === out; 12 | } 13 | 14 | component main = ascii_digits_test( 15 | 20 16 | ); 17 | -------------------------------------------------------------------------------- /circuit/tests/arrays/select_array_value_small_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/SelectArrayValue.circom"; 4 | 5 | template select_array_value_test(len) { 6 | signal input array[len]; 7 | signal input index; 8 | signal input expected_output; 9 | 10 | signal out <== SelectArrayValue(len)(array, index); 11 | out === expected_output; 12 | } 13 | 14 | component main = select_array_value_test( 15 | 1 16 | ); 17 | -------------------------------------------------------------------------------- /circuit/tests/arrays/array_selector_test_small.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/ArraySelector.circom"; 4 | 5 | template array_selector_test(len) { 6 | signal input start_index; 7 | signal input end_index; 8 | signal input expected_output[len]; 9 | 10 | signal out[len] <== ArraySelector(len)(start_index, end_index); 11 | out === expected_output; 12 | } 13 | 14 | component main = array_selector_test( 15 | 2 16 | ); 17 | -------------------------------------------------------------------------------- /circuit/tests/arrays/ascii_digits_to_scalar_small_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/AsciiDigitsToScalar.circom"; 4 | 5 | template ascii_digits_test(maxLen) { 6 | signal input digits[maxLen]; 7 | signal input len; 8 | signal input expected_output; 9 | 10 | signal out <== AsciiDigitsToScalar(maxLen)(digits, len); 11 | expected_output === out; 12 | } 13 | 14 | component main = ascii_digits_test( 15 | 2 16 | ); 17 | -------------------------------------------------------------------------------- /circuit/tests/arrays/select_array_value_large_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/SelectArrayValue.circom"; 4 | 5 | template select_array_value_test(len) { 6 | signal input array[len]; 7 | signal input index; 8 | signal input expected_output; 9 | 10 | signal out <== SelectArrayValue(len)(array, index); 11 | out === expected_output; 12 | } 13 | 14 | component main = select_array_value_test( 15 | 2000 16 | ); 17 | -------------------------------------------------------------------------------- /circuit/tests/arrays/array_selector_test_large.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/ArraySelector.circom"; 4 | 5 | template array_selector_test(len) { 6 | signal input start_index; 7 | signal input end_index; 8 | signal input expected_output[len]; 9 | 10 | signal out[len] <== ArraySelector(len)(start_index, end_index); 11 | out === expected_output; 12 | } 13 | 14 | component main = array_selector_test( 15 | 2000 16 | ); 17 | -------------------------------------------------------------------------------- /circuit/tests/packing/num2bits_be_test.circom: -------------------------------------------------------------------------------- 1 | 2 | pragma circom 2.2.2; 3 | 4 | include "helpers/packing/Num2BigEndianBits.circom"; 5 | 6 | template num2bits_be_test() { 7 | var max_bits_len = 8; 8 | signal input num_in; 9 | signal input bits_out[max_bits_len]; 10 | component num2bits_be = Num2BigEndianBits(max_bits_len); 11 | num2bits_be.in <== num_in; 12 | num2bits_be.out === bits_out; 13 | 14 | } 15 | 16 | component main = num2bits_be_test(); 17 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/logging.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LOGGING_HPP 2 | #define LOGGING_HPP 3 | 4 | #ifdef USE_LOGGER 5 | 6 | # include "logger.hpp" 7 | 8 | using namespace CPlusPlusLogging; 9 | 10 | #else 11 | 12 | # define LOG_ERROR(x) 13 | # define LOG_ALARM(x) 14 | # define LOG_ALWAYS(x) 15 | # define LOG_INFO(x) 16 | # define LOG_BUFFER(x) 17 | # define LOG_TRACE(x) 18 | # define LOG_DEBUG(x) 19 | 20 | #endif // USE_LOGGER 21 | 22 | #endif // LOGGING_HPP 23 | -------------------------------------------------------------------------------- /circuit/tests/misc/brackets_map_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/jwt/BracketsMap.circom"; 4 | 5 | template brackets_map_test() { 6 | var len = 13; 7 | signal input in[len]; 8 | signal input brackets[len]; 9 | component brackets_map = BracketsMap(len); 10 | brackets_map.arr <== in; 11 | for (var i = 0; i < len; i++) { 12 | brackets[i] === brackets_map.brackets[i]; 13 | } 14 | } 15 | 16 | component main = brackets_map_test(); 17 | -------------------------------------------------------------------------------- /circuit/tests/arrays/ascii_digits_to_scalar_large_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/AsciiDigitsToScalar.circom"; 4 | 5 | template ascii_digits_to_scalar_test(maxLen) { 6 | signal input digits[maxLen]; 7 | signal input len; 8 | signal input expected_output; 9 | 10 | signal out <== AsciiDigitsToScalar(maxLen)(digits, len); 11 | expected_output === out; 12 | } 13 | 14 | component main = ascii_digits_to_scalar_test( 15 | 2000 16 | ); 17 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/subprojects/json.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory = json-3.2.0 3 | source_url = https://github.com/nlohmann/json/archive/v3.2.0/json-3.2.0.tar.gz 4 | source_filename = json-3.2.0.tar.gz 5 | source_hash = 2de558ff3b3b32eebfb51cf2ceb835a0fa5170e6b8712b02be9c2c07fcfe52a1 6 | patch_url = https://wrapdb.mesonbuild.com/v2/json_3.2.0-1/get_patch 7 | patch_filename = json-3.2.0-1-wrap.zip 8 | patch_hash = f601837156f1f391ea9923c6d602450b1357537b1821d727251e75d97cb10f67 9 | 10 | -------------------------------------------------------------------------------- /circuit/tests/bigint/big_less_than_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/bigint/BigLessThan.circom"; 4 | 5 | template big_less_than_test() { 6 | signal input a[32]; 7 | signal input b[32]; 8 | signal input expected_output; 9 | component c1 = BigLessThan(252, 32); // `keyless` circom template's usage 10 | c1.a <== a; 11 | c1.b <== b; 12 | expected_output === c1.out; 13 | log("hi", c1.out); 14 | } 15 | 16 | component main = big_less_than_test(); 17 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/test.txt: -------------------------------------------------------------------------------- 1 | alt_bn128.cpp 2 | alt_bn128_test.cpp 3 | binfile_utils.cpp 4 | curve.cpp 5 | f2field.cpp 6 | fft.cpp 7 | fft_old.cpp 8 | fq.cpp 9 | fq_generic.cpp 10 | fq_raw_generic.cpp 11 | fr.cpp 12 | fr_generic.cpp 13 | fr_raw_generic.cpp 14 | fullprover.cpp 15 | groth16.cpp 16 | logger.cpp 17 | main.cpp 18 | misc.cpp 19 | multiexp.cpp 20 | naf.cpp 21 | scalar.cpp 22 | splitparstr.cpp 23 | splitparstr_test.cpp 24 | test_prover.cpp 25 | wtns_utils.cpp 26 | zkey_utils.cpp 27 | -------------------------------------------------------------------------------- /circuit/tests/arrays/array_selector_complex_small_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/ArraySelectorComplex.circom"; 4 | 5 | template array_selector_complex_test(len) { 6 | signal input start_index; 7 | signal input end_index; 8 | signal input expected_output[len]; 9 | 10 | signal out[len] <== ArraySelectorComplex(len)(start_index, end_index); 11 | out === expected_output; 12 | } 13 | 14 | component main = array_selector_complex_test( 15 | 3 16 | ); 17 | -------------------------------------------------------------------------------- /circuit/tests/arrays/elementwise_mul_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "stdlib/circuits/ElementwiseMul.circom"; 4 | 5 | template elementwise_mul_test(len) { 6 | signal input left[len]; 7 | signal input right[len]; 8 | signal input expected_out[len]; 9 | 10 | signal out[len] <== ElementwiseMul(len)(left, right); 11 | for (var i = 0; i < len; i++) { 12 | out[i] === expected_out[i]; 13 | } 14 | } 15 | 16 | component main = elementwise_mul_test( 17 | 4 18 | ); 19 | -------------------------------------------------------------------------------- /circuit/tests/arrays/array_selector_complex_large_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/ArraySelectorComplex.circom"; 4 | 5 | template array_selector_complex_test(len) { 6 | signal input start_index; 7 | signal input end_index; 8 | signal input expected_output[len]; 9 | 10 | signal out[len] <== ArraySelectorComplex(len)(start_index, end_index); 11 | out === expected_output; 12 | } 13 | 14 | component main = array_selector_complex_test( 15 | 2000 16 | ); 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | formatted_input.json 3 | .idea 4 | groth16_vk.json 5 | keyless_config.json 6 | onchain_vk.json 7 | prover_request_payload.json 8 | __pycache__ 9 | rust-rapidsnark/rapidsnark/.cache 10 | *.o 11 | *.a 12 | rust-rapidsnark/rapidsnark/subprojects/oneTBB-2022.0.0 13 | rust-rapidsnark/rapidsnark/subprojects/json-3.2.0 14 | rust-rapidsnark/rapidsnark/subprojects/scope_guard-0.9.1 15 | rust-rapidsnark/rapidsnark/subprojects/packagecache 16 | .DS_Store 17 | main_constraints.json 18 | *.r1cs 19 | *.sym 20 | *.zkey 21 | -------------------------------------------------------------------------------- /circuit/templates/helpers/base64url/README.md: -------------------------------------------------------------------------------- 1 | # base64url templates 2 | 3 | This file implements the base64url scheme from [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515#appendix-C), which has **no padding**: it does not append `=` padding characters to the encoded text. 4 | 5 | A few more details are discussed [here](http://alinush.org/keyless#base64url). 6 | 7 | This file started as a modification of [zkEmail's base64 libraries](https://github.com/zkemail/zk-email-verify/blob/main/packages/circuits/helpers/base64.circom). 8 | -------------------------------------------------------------------------------- /circuit/tests/sha_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/sha/SHA2_256_Prepadded_Hash.circom"; 4 | 5 | template sha_test(max_num_blocks) { 6 | signal input padded_input_bits[max_num_blocks * 512]; 7 | signal input input_bit_len; 8 | signal input expected_digest_bits[256]; 9 | component c1 = SHA2_256_Prepadded_Hash(max_num_blocks); 10 | c1.in <== padded_input_bits; 11 | c1.tBlock <== (input_bit_len / 512) - 1; 12 | expected_digest_bits === c1.out; 13 | } 14 | 15 | component main = sha_test(4); 16 | -------------------------------------------------------------------------------- /prover-service/test_jwk.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | { 4 | "kty": "RSA", 5 | "n": "6S7asUuzq5Q_3U9rbs-PkDVIdjgmtgWreG5qWPsC9xXZKiMV1AiV9LXyqQsAYpCqEDM3XbfmZqGb48yLhb_XqZaKgSYaC_h2DjM7lgrIQAp9902Rr8fUmLN2ivr5tnLxUUOnMOc2SQtr9dgzTONYW5Zu3PwyvAWk5D6ueIUhLtYzpcB-etoNdL3Ir2746KIy_VUsDwAM7dhrqSK8U2xFCGlau4ikOTtvzDownAMHMrfE7q1B6WZQDAQlBmxRQsyKln5DIsKv6xauNsHRgBAKctUxZG8M4QJIx3S6Aughd3RZC4Ca5Ae9fd8L8mlNYBCrQhOZ7dS0f4at4arlLcajtw", 6 | "e": "AQAB", 7 | "kid": "test-rsa", 8 | "alg": "RS256" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /rust-rapidsnark/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-rapidsnark" 3 | description = "Rust bindings for RapidSnark" 4 | version = "0.1.0" 5 | 6 | # Workspace inherited keys 7 | authors = { workspace = true } 8 | edition = { workspace = true } 9 | homepage = { workspace = true } 10 | license = { workspace = true } 11 | publish = { workspace = true } 12 | repository = { workspace = true } 13 | rust-version = { workspace = true } 14 | 15 | [dependencies] 16 | thiserror = { workspace = true } 17 | 18 | [build-dependencies] 19 | bindgen = { workspace = true } 20 | -------------------------------------------------------------------------------- /circuit/templates/stdlib/functions/assert_bits_fit_scalar.circom: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: Alin Tomescu 3 | */ 4 | pragma circom 2.2.2; 5 | 6 | include "MAX_BITS.circom"; 7 | 8 | /** 9 | * Utility method used to make sure an N-bit *unsigned* number will fit in a scalar, * for the curently-selected circom scalar field. 10 | */ 11 | function assert_bits_fit_scalar(N) { 12 | var max_bits = MAX_BITS(); 13 | log("N: ", N); 14 | log("MAX_BITS(): ", max_bits); 15 | 16 | assert(N <= max_bits); 17 | 18 | return 0; // circom needs you to return! 19 | } 20 | -------------------------------------------------------------------------------- /circuit/tests/arrays/assert_is_substring_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/IsSubstring.circom"; 4 | 5 | template assert_is_substring_test(maxStrLen, maxSubstrLen) { 6 | signal input str[maxStrLen]; 7 | signal input str_hash; 8 | signal input substr[maxSubstrLen]; 9 | signal input substr_len; 10 | signal input start_index; 11 | 12 | AssertIsSubstring(maxStrLen, maxSubstrLen)(str, str_hash, substr, substr_len, start_index); 13 | } 14 | 15 | component main = assert_is_substring_test( 16 | 100, 20 17 | ); 18 | -------------------------------------------------------------------------------- /circuit/tests/packing/bytes_to_big_endian_bits_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/packing/Bytes2BigEndianBits.circom"; 4 | 5 | template bytes_to_bits_test() { 6 | var max_bytes_len = 10; 7 | var max_bits_len = max_bytes_len * 8; 8 | signal input bytes_in[max_bytes_len]; 9 | signal input bits_out[max_bits_len]; 10 | component bytes_to_bits = Bytes2BigEndianBits(max_bytes_len); 11 | bytes_to_bits.bytes <== bytes_in; 12 | bytes_to_bits.bits === bits_out; 13 | 14 | } 15 | 16 | component main = bytes_to_bits_test(); 17 | -------------------------------------------------------------------------------- /circuit/tests/array_selector_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/ArraySelector.circom"; 4 | 5 | template array_selector_test() { 6 | var len = 8; 7 | 8 | signal input start_index; 9 | signal input end_index; 10 | signal input expected_out[len]; 11 | 12 | component array_selector = ArraySelector(len); 13 | 14 | array_selector.start_index <== start_index; 15 | array_selector.end_index <== end_index; 16 | 17 | array_selector.out === expected_out; 18 | 19 | } 20 | 21 | component main = array_selector_test(); 22 | -------------------------------------------------------------------------------- /release-helper/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "release-helper" 3 | description = "Aptos Keyless Release Helper" 4 | version = "0.1.0" 5 | 6 | # Workspace inherited keys 7 | authors = { workspace = true } 8 | edition = { workspace = true } 9 | homepage = { workspace = true } 10 | license = { workspace = true } 11 | publish = { workspace = true } 12 | repository = { workspace = true } 13 | rust-version = { workspace = true } 14 | 15 | [dependencies] 16 | aptos-keyless-common = { workspace = true } 17 | clap = { workspace = true } 18 | serde_json = { workspace = true } 19 | -------------------------------------------------------------------------------- /circuit/tests/arrays/assert_is_substring_large_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/IsSubstring.circom"; 4 | 5 | template assert_is_substring_test(maxStrLen, maxSubstrLen) { 6 | signal input str[maxStrLen]; 7 | signal input str_hash; 8 | signal input substr[maxSubstrLen]; 9 | signal input substr_len; 10 | signal input start_index; 11 | 12 | AssertIsSubstring(maxStrLen, maxSubstrLen)(str, str_hash, substr, substr_len, start_index); 13 | } 14 | 15 | component main = assert_is_substring_test( 16 | 2000, 1000 17 | ); 18 | -------------------------------------------------------------------------------- /circuit/tests/arrays/assert_is_substring_no_padding_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/IsSubstring.circom"; 4 | 5 | template assert_is_substring_test(maxStrLen, maxSubstrLen) { 6 | signal input str[maxStrLen]; 7 | signal input str_hash; 8 | signal input substr[maxSubstrLen]; 9 | signal input substr_len; 10 | signal input start_index; 11 | 12 | AssertIsSubstring(maxStrLen, maxSubstrLen)(str, str_hash, substr, substr_len, start_index); 13 | } 14 | 15 | component main = assert_is_substring_test( 16 | 12, 11 17 | ); 18 | -------------------------------------------------------------------------------- /circuit/templates/helpers/packing/BigEndianBits2Num.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | // Like Bits2Num in [circomlib](https://github.com/iden3/circomlib/blob/master/circuits/bitify.circom), 4 | // except assumes bits[0] is the MSB while bits[N-1] is the LSB. 5 | template BigEndianBits2Num(N) { 6 | signal input in[N]; 7 | signal output out; 8 | 9 | var acc = 0; 10 | var pow2 = 1; 11 | 12 | for (var i = 0; i < N; i++) { 13 | var index = (N-1) - i; 14 | 15 | acc += in[index] * pow2; 16 | 17 | pow2 = pow2 + pow2; 18 | } 19 | 20 | acc ==> out; 21 | } 22 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/misc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace aptos 6 | { 7 | 8 | static inline uint32_t const tab32[32] = { 9 | 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 10 | 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31}; 11 | 12 | inline uint32_t log2(uint32_t value) 13 | { 14 | value |= value >> 1; 15 | value |= value >> 2; 16 | value |= value >> 4; 17 | value |= value >> 8; 18 | value |= value >> 16; 19 | return tab32[(uint32_t)(value * 0x07C4ACDD) >> 27]; 20 | } 21 | 22 | } // namespace aptos 23 | -------------------------------------------------------------------------------- /circuit/tests/arrays/array_selector_complex_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/arrays/ArraySelectorComplex.circom"; 4 | 5 | template array_selector_complex_test(len) { 6 | signal input start_index; 7 | signal input end_index; 8 | signal input expected_output[len]; 9 | 10 | signal out[len] <== ArraySelectorComplex(len)(start_index, end_index); 11 | for (var i =0; i < len; i++) { 12 | log(out[i]); 13 | log(expected_output[i]); 14 | } 15 | out === expected_output; 16 | } 17 | 18 | component main = array_selector_complex_test( 19 | 8 20 | ); 21 | -------------------------------------------------------------------------------- /circuit/tests/arrays/invert_binary_array_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "stdlib/circuits/InvertBinaryArray.circom"; 4 | 5 | template invert_binary_array_test(len) { 6 | signal input in[len]; 7 | signal input expected_out[len]; 8 | 9 | signal {binary} tagged[len]; 10 | for (var i = 0; i < len; i++) { 11 | tagged[i] <== in[i]; 12 | } 13 | 14 | signal out[len] <== InvertBinaryArray(len)(tagged); 15 | for (var i = 0; i < len; i++) { 16 | out[i] === expected_out[i]; 17 | } 18 | } 19 | 20 | component main = invert_binary_array_test( 21 | 4 22 | ); 23 | -------------------------------------------------------------------------------- /circuit/tests/base64_decode_test_short.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/base64url/Base64UrlDecode.circom"; 4 | 5 | template base64url_decode_test(maxJWTPayloadLen) { 6 | var max_ascii_jwt_payload_len = (3*maxJWTPayloadLen)\4; 7 | signal input jwt_payload[maxJWTPayloadLen]; 8 | signal input ascii_jwt_payload[max_ascii_jwt_payload_len]; 9 | component base64urldecode = Base64UrlDecode(max_ascii_jwt_payload_len); 10 | base64urldecode.in <== jwt_payload; 11 | ascii_jwt_payload === base64urldecode.out; 12 | 13 | } 14 | 15 | component main = base64url_decode_test( 16 | 4 17 | ); 18 | -------------------------------------------------------------------------------- /prover-service/config_local_testing.yml: -------------------------------------------------------------------------------- 1 | resources_dir: "~/.local/share/aptos-keyless/current_setups" 2 | jwk_issuers: 3 | - issuer_name: "https://accounts.google.com" 4 | issuer_jwk_url: "https://www.googleapis.com/oauth2/v3/certs" 5 | - issuer_name: "test.oidc.provider" 6 | issuer_jwk_url: "https://github.com/aptos-labs/aptos-core/raw/main/types/src/jwks/rsa/secure_test_jwk.json" 7 | - issuer_name: "test.federated.oidc.provider" 8 | issuer_jwk_url: "https://github.com/aptos-labs/aptos-core/raw/main/types/src/jwks/rsa/secure_test_jwk.json" 9 | enable_test_provider: true 10 | enable_federated_jwks: true 11 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/README.md: -------------------------------------------------------------------------------- 1 | # Aptos version of RapidSnark 2 | 3 | This c++ library is wrapped by the `rust-rapidsnark` crate, which is used 4 | by the `prover-service` crate to compute proofs. 5 | 6 | ## Building 7 | 8 | Running `cargo build` inside `rust-rapidsnark` or inside `prover-service` 9 | will internally build the c++ code in this directory. If you wish to build 10 | the c++ code separately for some reason, make sure your development 11 | environment is setup (see `../../README.md`), and then run the following 12 | inside the current directory: 13 | 14 | ```bash 15 | ./build_lib.sh 16 | ``` 17 | 18 | -------------------------------------------------------------------------------- /circuit/tests/arrays/is_substring_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/IsSubstring.circom"; 4 | 5 | template is_substring_test(maxStrLen, maxSubstrLen) { 6 | signal input str[maxStrLen]; 7 | signal input str_hash; 8 | signal input substr[maxSubstrLen]; 9 | signal input substr_len; 10 | signal input start_index; 11 | signal input expected_output; 12 | 13 | signal out <== IsSubstring(maxStrLen, maxSubstrLen)(str, str_hash, substr, substr_len, start_index); 14 | expected_output === out; 15 | } 16 | 17 | component main = is_substring_test( 18 | 100, 20 19 | ); 20 | -------------------------------------------------------------------------------- /circuit/tests/arrays/is_substring_large_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/IsSubstring.circom"; 4 | 5 | template is_substring_test(maxStrLen, maxSubstrLen) { 6 | signal input str[maxStrLen]; 7 | signal input str_hash; 8 | signal input substr[maxSubstrLen]; 9 | signal input substr_len; 10 | signal input start_index; 11 | signal input expected_output; 12 | 13 | signal out <== IsSubstring(maxStrLen, maxSubstrLen)(str, str_hash, substr, substr_len, start_index); 14 | expected_output === out; 15 | } 16 | 17 | component main = is_substring_test( 18 | 2000, 1000 19 | ); 20 | -------------------------------------------------------------------------------- /circuit/tests/arrays/is_substring_no_padding_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/IsSubstring.circom"; 4 | 5 | template is_substring_test(maxStrLen, maxSubstrLen) { 6 | signal input str[maxStrLen]; 7 | signal input str_hash; 8 | signal input substr[maxSubstrLen]; 9 | signal input substr_len; 10 | signal input start_index; 11 | signal input expected_output; 12 | 13 | signal out <== IsSubstring(maxStrLen, maxSubstrLen)(str, str_hash, substr, substr_len, start_index); 14 | expected_output === out; 15 | } 16 | 17 | component main = is_substring_test( 18 | 12, 11 19 | ); 20 | -------------------------------------------------------------------------------- /circuit/tests/arrays/is_substring_small_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/IsSubstring.circom"; 4 | 5 | template is_substring_test(maxStrLen, maxSubstrLen) { 6 | signal input str[maxStrLen]; 7 | signal input str_hash; 8 | signal input substr[maxSubstrLen]; 9 | signal input substr_len; 10 | signal input start_index; 11 | signal input expected_output; 12 | 13 | signal out <== IsSubstring(maxStrLen, maxSubstrLen)(str, str_hash, substr, substr_len, start_index); 14 | expected_output === out; 15 | } 16 | 17 | component main = is_substring_test( 18 | 100, 20 19 | ); 20 | -------------------------------------------------------------------------------- /circuit/tests/packing/bits2num_big_endian_test.circom: -------------------------------------------------------------------------------- 1 | 2 | 3 | pragma circom 2.2.2; 4 | 5 | include "helpers/packing/BigEndianBits2Num.circom"; 6 | 7 | template bits2num_big_endian_test() { 8 | var max_bits_len = 64; 9 | signal input bits_in[max_bits_len]; 10 | signal input num_out; 11 | component num2bits_be = BigEndianBits2Num(max_bits_len); 12 | num2bits_be.in <== bits_in; 13 | for (var i = 0; i < max_bits_len; i++ ) { 14 | log(num2bits_be.in[i]); 15 | } 16 | log("output:", num2bits_be.out); 17 | num2bits_be.out === num_out; 18 | 19 | } 20 | 21 | component main = bits2num_big_endian_test(); 22 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/fq_element.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FQ_ELEMENT_HPP 2 | #define FQ_ELEMENT_HPP 3 | 4 | #include 5 | 6 | #define Fq_N64 4 7 | #define Fq_SHORT 0x00000000 8 | #define Fq_MONTGOMERY 0x40000000 9 | #define Fq_SHORTMONTGOMERY 0x40000000 10 | #define Fq_LONG 0x80000000 11 | #define Fq_LONGMONTGOMERY 0xC0000000 12 | 13 | typedef uint64_t FqRawElement[Fq_N64]; 14 | 15 | typedef struct __attribute__((__packed__)) { 16 | int32_t shortVal; 17 | uint32_t type; 18 | FqRawElement longVal; 19 | } FqElement; 20 | 21 | typedef FqElement *PFqElement; 22 | 23 | #endif // FQ_ELEMENT_HPP 24 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/fr_element.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FR_ELEMENT_HPP 2 | #define FR_ELEMENT_HPP 3 | 4 | #include 5 | 6 | #define Fr_N64 4 7 | #define Fr_SHORT 0x00000000 8 | #define Fr_MONTGOMERY 0x40000000 9 | #define Fr_SHORTMONTGOMERY 0x40000000 10 | #define Fr_LONG 0x80000000 11 | #define Fr_LONGMONTGOMERY 0xC0000000 12 | 13 | typedef uint64_t FrRawElement[Fr_N64]; 14 | 15 | typedef struct __attribute__((__packed__)) { 16 | int32_t shortVal; 17 | uint32_t type; 18 | FrRawElement longVal; 19 | } FrElement; 20 | 21 | typedef FrElement *PFrElement; 22 | 23 | #endif // FR_ELEMENT_HPP 24 | -------------------------------------------------------------------------------- /circuit/tests/base64_decode_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/base64url/Base64UrlDecode.circom"; 4 | 5 | template base64url_decode_test(maxJWTPayloadLen) { 6 | var max_ascii_jwt_payload_len = (3*maxJWTPayloadLen)\4; 7 | signal input jwt_payload[maxJWTPayloadLen]; 8 | signal input ascii_jwt_payload[max_ascii_jwt_payload_len]; 9 | component base64urldecode = Base64UrlDecode(max_ascii_jwt_payload_len); 10 | base64urldecode.in <== jwt_payload; 11 | ascii_jwt_payload === base64urldecode.out; 12 | 13 | } 14 | 15 | component main = base64url_decode_test( 16 | 192*8-64 // maxJWTPayloadLen 17 | ); 18 | -------------------------------------------------------------------------------- /scripts/run_prover_service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This file contains a simple script for running the prover service locally. 4 | 5 | # Set appropriate script flags. 6 | set -ex 7 | 8 | # Procure a testing setup to generate the Groth16 proving key. 9 | # Note: this can take ~10 minutes the first time it is run. 10 | ./scripts/task.sh setup procure-testing-setup 11 | 12 | # Run the prover service. 13 | # TODO: handle the libtbb.dylib issue on macOS. 14 | cargo run -p prover-service -- \ 15 | --config-file-path ./prover-service/config_local_testing.yml \ 16 | --training-wheels-private-key-file-path ./prover-service/private_key_for_testing.txt 17 | -------------------------------------------------------------------------------- /circuit/templates/stdlib/functions/min_num_bits.circom: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: Alin Tomescu 3 | */ 4 | pragma circom 2.2.2; 5 | 6 | include "./log2_floor.circom"; 7 | 8 | /** 9 | * Returns the minimum # of bits needed to represent n. 10 | * 11 | * Note: Representing 0 is assumed to be done via 1 (zero) bit. 12 | * 13 | * Examples: 14 | * n = 0 --> 1 15 | * n = 1 --> 1 16 | * n = 2,3 --> 2 17 | * n = 4,5,6,7 --> 3 18 | * n \in [2^k, 2^{k+1}) --> k + 1 19 | */ 20 | function min_num_bits(n) { 21 | if(n == 0) { 22 | return 1; 23 | } 24 | 25 | return log2_floor(n) + 1; 26 | } -------------------------------------------------------------------------------- /circuit/templates/helpers/packing/AssertIsBytes.circom: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: Michael Straka, Alin Tomescu 3 | */ 4 | pragma circom 2.2.2; 5 | 6 | // for Num2Bits 7 | include "circomlib/circuits/bitify.circom"; 8 | 9 | // Enforces that each scalar in an array is a byte. 10 | // 11 | // @param NUM_BYTES the size of the input array 12 | // 13 | // @input in the input array of NUM_BYTES signals 14 | // 15 | // @postconditions in[i] \in [0, 256), \forall i \in [0, NUM_BYTES) 16 | template AssertIsBytes(NUM_BYTES) { 17 | signal input in[NUM_BYTES]; 18 | 19 | for (var i = 0; i < NUM_BYTES; i++) { 20 | _ <== Num2Bits(8)(in[i]); 21 | } 22 | } -------------------------------------------------------------------------------- /vk-diff/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aptos-keyless-vk-diff" 3 | description = "Aptos Keyless VK diff tool" 4 | version = "0.1.0" 5 | 6 | # Workspace inherited keys 7 | authors = { workspace = true } 8 | edition = { workspace = true } 9 | homepage = { workspace = true } 10 | license = { workspace = true } 11 | publish = { workspace = true } 12 | repository = { workspace = true } 13 | rust-version = { workspace = true } 14 | 15 | [dependencies] 16 | aptos-keyless-common = { workspace = true } 17 | clap = { workspace = true } 18 | serde_json = { workspace = true } 19 | strum_macros = { workspace = true } 20 | ureq = { workspace = true } 21 | url = { workspace = true } 22 | -------------------------------------------------------------------------------- /circuit/tests/arrays/assert_is_concatenation_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/AssertIsConcatenation.circom"; 4 | 5 | template concatenation_check_test(maxFullStringLen, maxLeftStringLen, maxRightStringLen) { 6 | signal input full_string[maxFullStringLen]; 7 | signal input left[maxLeftStringLen]; 8 | signal input right[maxRightStringLen]; 9 | signal input left_len; 10 | signal input right_len; 11 | 12 | AssertIsConcatenation(maxFullStringLen, maxLeftStringLen, maxRightStringLen)(full_string, left, right, left_len, right_len); 13 | } 14 | 15 | component main = concatenation_check_test( 16 | 100, 70, 70 17 | ); 18 | -------------------------------------------------------------------------------- /circuit/tests/arrays/assert_is_concatenation_small_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/AssertIsConcatenation.circom"; 4 | 5 | template concatenation_check_test(maxFullStringLen, maxLeftStringLen, maxRightStringLen) { 6 | signal input full_string[maxFullStringLen]; 7 | signal input left[maxLeftStringLen]; 8 | signal input right[maxRightStringLen]; 9 | signal input left_len; 10 | signal input right_len; 11 | 12 | AssertIsConcatenation(maxFullStringLen, maxLeftStringLen, maxRightStringLen)(full_string, left, right, left_len, right_len); 13 | } 14 | 15 | component main = concatenation_check_test( 16 | 2, 1, 1 17 | ); 18 | -------------------------------------------------------------------------------- /circuit/templates/helpers/packing/AssertIs64BitLimbs.circom: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: Michael Straka, Alin Tomescu 3 | */ 4 | pragma circom 2.2.2; 5 | 6 | // for Num2Bits 7 | include "circomlib/circuits/bitify.circom"; 8 | 9 | // Enforces that each scalar in an array is 64-bits. 10 | // 11 | // @param NUM_LIMBS the size of the input array 12 | // 13 | // @input in the input array of NUM_LIMBS signals 14 | // 15 | // @postconditions in[i] \in [0, 2^{64}), \forall i \in [0, NUM_LIMBS) 16 | template AssertIs64BitLimbs(NUM_LIMBS) { 17 | signal input in[NUM_LIMBS]; 18 | 19 | for (var i = 0; i < NUM_LIMBS; i++) { 20 | _ <== Num2Bits(64)(in[i]); 21 | } 22 | } -------------------------------------------------------------------------------- /circuit/templates/stdlib/circuits/InvertBinaryArray.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | // Given a binary array, returns an "inverted" array where where bits are flipped. 4 | // 5 | // @param LEN the length of the array 6 | // 7 | // @input in[LEN] {binary} the input array of bits 8 | // @output out[LEN] {binary} the output array of flipped bits 9 | // 10 | // @notes 11 | // Enforces at compile time that `in` contains only 1s and 0s via the {binary} tag. 12 | template InvertBinaryArray(LEN) { 13 | signal input {binary} in[LEN]; 14 | signal output {binary} out[LEN]; 15 | 16 | for (var i = 0; i < LEN; i++) { 17 | out[i] <== 1 - in[i]; 18 | } 19 | } -------------------------------------------------------------------------------- /circuit/tests/arrays/assert_is_concatenation_large_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/strings/AssertIsConcatenation.circom"; 4 | 5 | template concatenation_check_test(maxFullStringLen, maxLeftStringLen, maxRightStringLen) { 6 | signal input full_string[maxFullStringLen]; 7 | signal input left[maxLeftStringLen]; 8 | signal input right[maxRightStringLen]; 9 | signal input left_len; 10 | signal input right_len; 11 | 12 | log("silly test string"); 13 | AssertIsConcatenation(maxFullStringLen, maxLeftStringLen, maxRightStringLen)(full_string, left, right, left_len, right_len); 14 | } 15 | 16 | component main = concatenation_check_test( 17 | 1600, 1000, 1000 18 | ); 19 | -------------------------------------------------------------------------------- /circuit/templates/stdlib/functions/MAX_BITS.circom: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: Alin Tomescu 3 | */ 4 | pragma circom 2.2.2; 5 | 6 | /** 7 | * Computes the maximum bit-width $b$ such that an *unsigned* $2^b - 1$ value can 8 | * be always stored in a circom scalar in $\mathbb{Z}_p$, without it wrapping around 9 | * after being reduced modulo $p$. 10 | * 11 | * Leverages the fact that circom comparison operators treat scalars in $[0, p/2]$ 12 | * as positive, while every $v \in (p/2, p)$ is treated as negative (i.e., as 13 | * $v - p$ instead of as $v$). 14 | */ 15 | function MAX_BITS() { 16 | var n = 1; 17 | var b = 1; 18 | 19 | while (2 * n > n) { 20 | n = n * 2; 21 | b = b + 1; 22 | } 23 | 24 | return b; 25 | } -------------------------------------------------------------------------------- /prover-service/src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Aptos Foundation 2 | 3 | use aptos_logger::error; 4 | use serde::{Deserialize, Serialize}; 5 | use thiserror::Error; 6 | 7 | /// A prover service error (e.g., for bad API requests, internal errors, etc.) 8 | #[derive(Clone, Debug, Deserialize, Error, PartialEq, Eq, Serialize)] 9 | pub enum ProverServiceError { 10 | #[error("Bad request error: {0}")] 11 | BadRequest(String), 12 | #[error("Internal service error: {0}")] 13 | InternalError(String), 14 | #[error("Unexpected error: {0}")] 15 | UnexpectedError(String), 16 | } 17 | 18 | impl From for ProverServiceError { 19 | fn from(error: anyhow::Error) -> Self { 20 | ProverServiceError::UnexpectedError(error.to_string()) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /circuit/tests/assert_is_substring_test.circom: -------------------------------------------------------------------------------- 1 | 2 | pragma circom 2.2.2; 3 | 4 | include "helpers/strings/IsSubstring.circom"; 5 | 6 | template assert_is_substring_test() { 7 | var max_str_len = 256; 8 | var max_substr_len = 8; 9 | 10 | signal input str[max_str_len]; 11 | signal input str_hash; 12 | signal input substr[max_substr_len]; 13 | signal input substr_len; 14 | signal input start_index; 15 | 16 | component assert_is_substring = AssertIsSubstring(max_str_len, max_substr_len); 17 | 18 | assert_is_substring.str <== str; 19 | assert_is_substring.str_hash <== str_hash; 20 | assert_is_substring.substr <== substr; 21 | assert_is_substring.substr_len <== substr_len; 22 | assert_is_substring.start_index <== start_index; 23 | } 24 | 25 | component main = assert_is_substring_test(); 26 | -------------------------------------------------------------------------------- /circuit/templates/helpers/arrays/SelectArrayValue.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "./SingleOneArray.circom"; 4 | 5 | include "circomlib/circuits/multiplexer.circom"; 6 | 7 | // Indexes into an array of signals, returning the value at that index. 8 | // 9 | // @param LEN the length of the array 10 | // 11 | // @input arr[LEN] the array of length `LEN` 12 | // @input i the location in the array to be fetched; must have i \in [0, LEN) 13 | // @output out arr[i] 14 | // 15 | // TODO(Buses): Use an Index(LEN) bus here to ensure `0 <= i < LEN`. 16 | // TODO: Rename to ArrayGet 17 | template SelectArrayValue(LEN) { 18 | signal input arr[LEN]; 19 | signal input i; 20 | signal output out; 21 | 22 | signal mask[LEN] <== SingleOneArray(LEN)(i); 23 | 24 | out <== EscalarProduct(LEN)(arr, mask); 25 | } -------------------------------------------------------------------------------- /circuit/tests/misc/brackets_depth_map_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "helpers/jwt/BracketsDepthMap.circom"; 4 | 5 | template brackets_depth_map_test() { 6 | var len = 15; 7 | signal input in[len]; 8 | signal input brackets[len]; 9 | component brackets_depth_map = BracketsDepthMap(len); 10 | brackets_depth_map.arr <== in; 11 | for (var i = 0; i < len; i++) { 12 | log("out ", i, ": ", brackets_depth_map.out[i]); 13 | } 14 | for (var i = 0; i < len; i++) { 15 | log("in ", i, ": ", in[i]); 16 | } 17 | for (var i = 0; i < len; i++) { 18 | log("expected result ", i, ": ", brackets[i]); 19 | } 20 | for (var i = 0; i < len; i++) { 21 | brackets[i] === brackets_depth_map.out[i]; 22 | } 23 | } 24 | 25 | component main = brackets_depth_map_test(); 26 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/exp2.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "naf.hpp" 5 | 6 | template 7 | void exp(Field::Element& res, Field::Element& base, uint8_t* scalar, unsigned int scalarSize) { 8 | Field::Element baseCopy; 9 | int nBits = (scalarSize*8)+2; 10 | uint8_t *naf = new uint8_t[(scalarSize+2)*8]; 11 | buildNaf(naf, scalar, scalarSize); 12 | 13 | G.copy(baseCopy, base); // base and result can be the same 14 | G.copy(r, G.zero()); 15 | int i = nBits-1; 16 | while ((i>=0)&&(naf[i] == 0)) i--; 17 | while (i>=0) { 18 | G.dbl(r, r); 19 | if (naf[i] == 1) { 20 | G.add(r, r, baseCopy); 21 | } else if (naf[i] == 2) { 22 | G.sub(r, r, baseCopy); 23 | } 24 | i--; 25 | } 26 | 27 | delete [] naf; 28 | } 29 | -------------------------------------------------------------------------------- /circuit/tests/packing/big_endian_bits_to_scalars_test.circom: -------------------------------------------------------------------------------- 1 | 2 | pragma circom 2.2.2; 3 | 4 | include "helpers/packing/BigEndianBitsToScalars.circom"; 5 | 6 | template bits_to_field_elems_test() { 7 | var max_bits_len = 256; 8 | var bits_per_field_elem = 64; 9 | var num_field_elems = max_bits_len%bits_per_field_elem == 0 ? max_bits_len \ bits_per_field_elem : (max_bits_len\bits_per_field_elem) + 1; // '\' is the quotient operation - we add 1 if there are extra bits past the full bytes 10 | signal input bits_in[max_bits_len]; 11 | signal input field_elems_out[num_field_elems]; 12 | component bits_to_field_elems = BigEndianBitsToScalars(max_bits_len, bits_per_field_elem); 13 | bits_to_field_elems.in <== bits_in; 14 | bits_to_field_elems.elems === field_elems_out; 15 | 16 | } 17 | 18 | component main = bits_to_field_elems_test(); 19 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/random_generator.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RANDOM_GENERATOR_H 2 | #define RANDOM_GENERATOR_H 3 | 4 | #ifdef USE_SODIUM 5 | 6 | # include 7 | 8 | #else 9 | 10 | # include 11 | 12 | inline void randombytes_buf(void* const buf, const size_t size) 13 | { 14 | std::random_device engine; 15 | std::uniform_int_distribution distr; 16 | 17 | uint8_t* buffer = static_cast(buf); 18 | 19 | for (size_t i = 0; i < size; i++) 20 | { 21 | buffer[i] = distr(engine); 22 | } 23 | } 24 | 25 | #endif // USE_SODIUM 26 | 27 | #include 28 | 29 | template 30 | inline void fill_with_random_bytes(T& x) 31 | { 32 | static_assert(std::is_pod_v); 33 | randombytes_buf(std::addressof(x), sizeof(x)); 34 | } 35 | 36 | #endif // RANDOM_GENERATOR_H 37 | -------------------------------------------------------------------------------- /keyless-common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aptos-keyless-common" 3 | description = "Aptos Keyless common modules" 4 | version = "0.1.0" 5 | 6 | # Workspace inherited keys 7 | authors = { workspace = true } 8 | edition = { workspace = true } 9 | homepage = { workspace = true } 10 | license = { workspace = true } 11 | publish = { workspace = true } 12 | repository = { workspace = true } 13 | rust-version = { workspace = true } 14 | 15 | [dependencies] 16 | anyhow = { workspace = true } 17 | aptos-logger = { workspace = true } 18 | aptos-types = { workspace = true } 19 | ark-bn254 = { workspace = true } 20 | ark-ff = { workspace = true } 21 | ark-serialize = { workspace = true } 22 | base64 = { workspace = true } 23 | hex = { workspace = true } 24 | num-bigint = { workspace = true } 25 | num-traits = { workspace = true } 26 | serde = { workspace = true } 27 | serde_json = { workspace = true } 28 | -------------------------------------------------------------------------------- /scripts/python/prover_service.py: -------------------------------------------------------------------------------- 1 | from utils import manage_deps 2 | import utils 3 | import typer 4 | 5 | app = typer.Typer(no_args_is_help=True) 6 | 7 | @app.command() 8 | def install_deps(): 9 | """install the dependencies for building and running the prover service.""" 10 | manage_deps.install_deps(["pkg-config", "lld", "meson", "rust", "clang", "cmake", "make", "libyaml", "nasm", "gmp", "openssl"]) 11 | 12 | @app.command() 13 | def add_envvars_to_profile(): 14 | """Add the directory containing libtbb to LD_LIBRARY_PATH. Required for running the prover service and for running the prover service tests.""" 15 | path = utils.repo_root() / "rust-rapidsnark/rapidsnark/build/subprojects/oneTBB-2022.0.0" 16 | utils.add_envvar_to_profile("LD_LIBRARY_PATH", "$LD_LIBRARY_PATH:" + str(path)) 17 | utils.add_envvar_to_profile("DYLD_LIBRARY_PATH", "$DYLD_LIBRARY_PATH:" + str(path)) 18 | -------------------------------------------------------------------------------- /circuit/templates/stdlib/circuits/ElementwiseMul.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | // Returns the elementwise (Hadamard) product of two arrays: i.e., another array whose 4 | // ith entry is the product of the ith entries in the two arrays. 5 | // 6 | // @param LEN the length of the two input arrays 7 | // 8 | // @input lhs the first input array 9 | // @input rhs the second input array 10 | // 11 | // @output out the Hadamard product of the two arrays 12 | // 13 | // @warning this could cause integer overflow if the product of any two multiplied 14 | // elements exceeds the field modulus 15 | // 16 | // TODO: rename to HadamardProduct or ElementwiseProduct 17 | template ElementwiseMul(LEN) { 18 | signal input lhs[LEN]; 19 | signal input rhs[LEN]; 20 | signal output out[LEN]; 21 | 22 | for (var i = 0; i < LEN; i++) { 23 | out[i] <== lhs[i] * rhs[i]; 24 | } 25 | } -------------------------------------------------------------------------------- /circuit/templates/helpers/packing/Bytes2BigEndianBits.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "./Num2BigEndianBits.circom"; 4 | 5 | // Converts a byte array to a bit array, where the each byte is converted into a 6 | // big-endian bits. 7 | // 8 | // @input bytes an array of bytes 9 | // 10 | // @output bits an array of bits, where bits[i * 8], ..., bits[(i * 8) + 7] 11 | // are the bits in bytes[i], with bits[i * 8] being the MSB 12 | template Bytes2BigEndianBits(LEN) { 13 | signal input bytes[LEN]; 14 | signal output bits[8 * LEN]; 15 | 16 | component num2bits[LEN]; 17 | for (var i = 0; i < LEN; i++) { 18 | num2bits[i] = Num2BigEndianBits(8); 19 | num2bits[i].in <== bytes[i]; 20 | 21 | for (var j = 0; j < 8; j++) { 22 | var IDX = (i * 8) + j; 23 | num2bits[i].out[j] ==> bits[IDX]; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # Pre-commit configuration file. For more information, see: 2 | # - https://pre-commit.com and https://pre-commit.com/hooks.html 3 | repos: 4 | - repo: https://github.com/pre-commit/pre-commit-hooks 5 | rev: v4.5.0 6 | hooks: 7 | - id: trailing-whitespace 8 | files: \.(rs|move)$ 9 | - id: end-of-file-fixer 10 | files: \.(rs|move)$ 11 | - id: check-added-large-files 12 | args: 13 | - --maxkb=2000 14 | - repo: https://github.com/aptos-labs/pre-commit-hooks 15 | rev: a30f0d816e5062a67d87c8de753cfe499672b959 # Fix the revision to the v1.5.5 release commit. See: https://github.com/Lucas-C/pre-commit-hooks/releases/tag/v1.5.5 16 | hooks: 17 | - id: insert-license 18 | files: .*\.rs$ 19 | args: 20 | - --license-filepath 21 | - license_header.txt 22 | - --comment-style 23 | - // 24 | -------------------------------------------------------------------------------- /circuit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aptos-keyless-circuit-testbed", 3 | "version": "1.0.0", 4 | "description": "Testing and benchmarking suit for the Aptos Keyless circom circuit", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "benches" 8 | }, 9 | "scripts": { 10 | "test": "NODE_OPTIONS=--enable-source-maps CIRCOMLIB_PATH=`npm root -g` mocha 'benches/*.js' 'benches/**/*.js' --max-old-space-size=4000" 11 | }, 12 | "keywords": [ 13 | "circuit", 14 | "circom", 15 | "zksnark", 16 | "testing", 17 | "benchmarks" 18 | ], 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/aptos-labs/keyless-zk-proofs.git" 22 | }, 23 | "author": "Alin Tomescu", 24 | "license": "Apache-2.0", 25 | "devDependencies": { 26 | "@noble/hashes": "^1.7.1", 27 | "chai": "^4.3.4", 28 | "circom_tester": "0.0.23", 29 | "mocha": "^11.1.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /circuit/templates/helpers/jwt/EnforceNotNested.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "../arrays/ArraySelector.circom"; 4 | 5 | include "circomlib/circuits/multiplexer.circom"; 6 | 7 | // Given an input `brackets_depth_map`, which must be an output of `BracketsDepthMap` and 8 | // corresponds to the nested brackets depth of the original JWT, and a `start_index` and `field_len` 9 | // corresponding to the first index and length of a full field in the JWT, fails if the given field 10 | // contains any indices inside nested brackets in the original JWT, and succeeds otherwise 11 | template EnforceNotNested(LEN) { 12 | signal input start_index; 13 | signal input field_len; 14 | signal input brackets_depth_map[LEN]; 15 | 16 | signal brackets_selector[LEN] <== ArraySelector(LEN)(start_index, start_index + field_len); 17 | signal is_nested <== EscalarProduct(LEN)(brackets_depth_map, brackets_selector); 18 | is_nested === 0; 19 | } 20 | -------------------------------------------------------------------------------- /circuit/templates/stdlib/functions/log2_floor.circom: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: Alin Tomescu 3 | */ 4 | pragma circom 2.2.2; 5 | 6 | /** 7 | * Given a natural number n > 0, returns \floor{\log_2{n}}. 8 | * 9 | * Arguments: 10 | * n non-zero, natural number 11 | * (presumed not to have been >= p and thus not to have been wrapped around) 12 | * 13 | * Returns: 14 | * \floor{\log_2{n}} 15 | * 16 | * Examples: 17 | * n = 0 --> undefined 18 | * n = 1 --> 0 19 | * n = 2,3 --> 1 20 | * n = 4,5,6,7 --> 2 21 | * n = 8,9,10,11,12,13,14,15 --> 3 22 | */ 23 | function log2_floor(n) { 24 | assert(n != 0); 25 | 26 | var log2 = 0; 27 | 28 | // WARNING: Do not use <, >, <=, >= comparison operators here or you will 29 | // suffer from circom's signed numbers semantics! 30 | while (n != 1) { 31 | log2 += 1; 32 | n \= 2; 33 | } 34 | 35 | return log2; 36 | } -------------------------------------------------------------------------------- /prover-service/circuit_config.yml: -------------------------------------------------------------------------------- 1 | has_input_skip_aud_checks: true 2 | max_lengths: 3 | b64u_jwt_no_sig_sha2_padded: 1536 4 | b64u_jwt_header_w_dot: 300 5 | b64u_jwt_payload_sha2_padded: 1472 6 | b64u_jwt_payload: 1472 7 | sha2_num_bits: 8 8 | sha2_padding: 64 # TODO: Why do we need a constant for this? It is standardized that SHA2-padding is at most 64 bytes (512 bits)... 9 | epk: 3 10 | iss_field: 140 11 | iss_field_string_bodies: 140 12 | iss_name: 40 13 | iss_value: 120 14 | extra_field: 350 15 | ev_field: 30 16 | ev_name: 20 17 | ev_value: 10 18 | nonce_field: 105 19 | nonce_field_string_bodies: 105 20 | nonce_name: 10 21 | nonce_value: 100 22 | aud_field: 140 23 | aud_field_string_bodies: 140 24 | aud_name: 40 25 | private_aud_value: 120 26 | override_aud_value: 120 27 | iat_field: 50 28 | iat_name: 10 29 | iat_value: 45 30 | uid_field: 350 31 | uid_field_string_bodies: 350 32 | uid_name: 30 33 | uid_value: 330 34 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/spinlock.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace aptos 6 | { 7 | 8 | class [[nodiscard]] spinlock 9 | { 10 | public: 11 | using lockable_type = std::atomic_uint; 12 | 13 | [[nodiscard]] constexpr spinlock() noexcept = default; 14 | 15 | spinlock(const spinlock&) noexcept = delete; 16 | spinlock& operator=(const spinlock&) noexcept = delete; 17 | 18 | void lock() noexcept 19 | { 20 | while (lockable_.exchange(1u, std::memory_order_acquire)) 21 | { 22 | while (lockable_.load(std::memory_order_relaxed)) 23 | { 24 | } 25 | } 26 | } 27 | 28 | bool try_lock() noexcept 29 | { 30 | return lockable_.exchange(1u, std::memory_order_acquire); 31 | } 32 | 33 | void unlock() noexcept { lockable_.store(0u, std::memory_order_release); } 34 | 35 | private: 36 | std::atomic_uint lockable_{0u}; 37 | }; 38 | 39 | } // namespace aptos 40 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/exp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "naf.hpp" 8 | 9 | template 10 | void nafMulByScalar(BaseGroup &G, BaseGroupElementOut& r, BaseGroupElementIn& base, uint8_t* scalar, unsigned int scalarSize) { 11 | BaseGroupElementIn baseCopy; 12 | int nBits = (scalarSize*8)+2; 13 | uint8_t *naf = new uint8_t[(scalarSize+2)*8]; 14 | buildNaf(naf, scalar, scalarSize); 15 | 16 | G.copy(baseCopy, base); // base and result can be the same 17 | G.copy(r, G.zero()); 18 | int i = nBits-1; 19 | while ((i>=0)&&(naf[i] == 0)) i--; 20 | while (i>=0) { 21 | G.dbl(r, r); 22 | if (naf[i] == 1) { 23 | G.add(r, r, baseCopy); 24 | } else if (naf[i] == 2) { 25 | G.sub(r, r, baseCopy); 26 | } 27 | i--; 28 | } 29 | 30 | delete[] naf; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /circuit/templates/helpers/arrays/ArraySelectorComplex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "./LeftArraySelector.circom"; 4 | include "./RightArraySelector.circom"; 5 | 6 | include "circomlib/circuits/comparators.circom"; 7 | 8 | // Like ArraySelector, but works when end_idx > start_idx is not satisfied, in which 9 | // case an array of all 0s is returned. Does NOT work when start_idx is 0. 10 | // 11 | // TODO: Rename to something more clear 12 | // TODO: "Does not work when start_idx = 0" is just an artifact or something done on purpose? 13 | template ArraySelectorComplex(LEN) { 14 | signal input start_idx; 15 | signal input end_idx; 16 | signal output out[LEN]; 17 | 18 | signal start_idx_is_zero <== IsZero()(start_idx); 19 | start_idx_is_zero === 0; 20 | 21 | signal right_bits[LEN] <== RightArraySelector(LEN)(start_idx - 1); 22 | signal left_bits[LEN] <== LeftArraySelector(LEN)(end_idx); 23 | 24 | for (var i = 0; i < LEN; i++) { 25 | out[i] <== right_bits[i] * left_bits[i]; 26 | } 27 | } -------------------------------------------------------------------------------- /circuit/tests/jwt_field_parsing/email_verified_check_test.circom: -------------------------------------------------------------------------------- 1 | 2 | pragma circom 2.2.2; 3 | 4 | include "helpers/jwt/EmailVerifiedCheck.circom"; 5 | 6 | template email_verified_check_test() { 7 | var maxEVNameLen = 20; 8 | var maxEVValueLen = 10; 9 | var maxUIDNameLen = 30; 10 | signal input ev_name[maxEVNameLen]; 11 | signal input ev_value[maxEVValueLen]; 12 | signal input ev_value_len; 13 | signal input uid_name[maxUIDNameLen]; 14 | signal input uid_name_len; 15 | signal input uid_is_email; 16 | component email_verified_check = EmailVerifiedCheck(maxEVNameLen, maxEVValueLen, maxUIDNameLen); 17 | email_verified_check.ev_name <== ev_name; 18 | email_verified_check.ev_value <== ev_value; 19 | email_verified_check.ev_value_len <== ev_value_len; 20 | email_verified_check.uid_name <== uid_name; 21 | email_verified_check.uid_name_len <== uid_name_len; 22 | email_verified_check.uid_is_email === uid_is_email; 23 | 24 | } 25 | 26 | component main = email_verified_check_test( 27 | ); 28 | 29 | 30 | -------------------------------------------------------------------------------- /circuit/templates/stdlib/circuits/Sum.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | // This circuit returns the sum of an array of signals. 4 | // 5 | // @param N the size of the array 6 | // 7 | // @input nums[N] the array of signals 8 | // @output sum the sum of the signals in the array 9 | // 10 | // @notes: 11 | // Originally, Michael implemented it like [this](https://github.com/TheFrozenFire/snark-jwt-verify/blob/master/circuits/calculate_total.circom). But this seems really 12 | // inefficient (famous last words) I am not sure that the compiler optimizes it away. 13 | // The circom paper clearly shows that a var suffices here (see the MultiAND example 14 | // in Section 3.12 of [the paper](https://www.techrxiv.org/articles/preprint/CIRCOM_A_Robust_and_Scalable_Language_for_Building_Complex_Zero-Knowledge_Circuits/19374986)) 15 | template Sum(N) { 16 | signal input nums[N]; 17 | signal output sum; 18 | 19 | var lc = 0; 20 | 21 | for (var i = 0; i < N; i++) { 22 | lc += nums[i]; 23 | } 24 | 25 | sum <== lc; 26 | } -------------------------------------------------------------------------------- /scripts/python/misc.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | import utils 3 | from utils import eprint 4 | import os 5 | import stat 6 | import pathlib 7 | import typer 8 | 9 | app = typer.Typer(no_args_is_help=True) 10 | 11 | 12 | @app.command() 13 | def compute_sample_proof(): 14 | """UNIMPLEMENTED""" 15 | eprint("compute_sample_proof") 16 | eprint("Not yet implemented") 17 | exit(2) 18 | 19 | 20 | @app.command() 21 | def install_circom_precommit_hook(): 22 | """Install a pre-commit hook that requires the main circuit to compile before committing.""" 23 | 24 | eprint("Installing precommit hook...") 25 | 26 | hook_src_path = utils.repo_root() + "/git-hooks/compile-circom-if-needed-pre-commit" 27 | hook_dest_path = utils.repo_root() + "/.git/hooks/pre-commit" 28 | eprint(hook_src_path) 29 | eprint(hook_dest_path) 30 | 31 | 32 | 33 | pathlib.Path(hook_dest_path).unlink(True) 34 | shutil.copyfile(hook_src_path, hook_dest_path) 35 | os.chmod(hook_dest_path, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR) 36 | 37 | eprint("Done.") 38 | 39 | 40 | -------------------------------------------------------------------------------- /circuit/templates/helpers/arrays/SingleOneArray.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | // Returns a "one-hot" bit mask with a 1 at index `idx`, and 0s everywhere else. 4 | // Only satisfiable when 0 <= idx < LEN. 5 | // 6 | // @param LEN the length of the mask 7 | // 8 | // @input idx the index \in [0, LEN) where the bitmask should be 1 9 | // @output out[LEN] the "one-hot" bit mask 10 | // 11 | // @notes 12 | // Similar to Decoder template from [circomlib](https://github.com/iden3/circomlib/blob/35e54ea21da3e8762557234298dbb553c175ea8d/circuits/multiplexer.circom#L78), except 13 | // it does NOT return all zeros when idx > LEN. 14 | template SingleOneArray(LEN) { 15 | signal input idx; 16 | signal output out[LEN]; 17 | 18 | signal success; 19 | var lc = 0; 20 | 21 | for (var i = 0; i < LEN; i++) { 22 | out[i] <-- (idx == i) ? 1 : 0; 23 | // C1: Enforces that either: out[i] == 0, or idx == i 24 | out[i] * (idx - i) === 0; 25 | lc = lc + out[i]; 26 | } 27 | lc ==> success; 28 | 29 | // C2: Enforces that `lc` is equal to 1 30 | success === 1; 31 | } -------------------------------------------------------------------------------- /circuit/templates/helpers/jwt/BracketsMap.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "circomlib/circuits/comparators.circom"; 4 | 5 | // Given an array of ASCII characters `arr`, returns an array `brackets` with 6 | // a 1 in the position of each open bracket `{`, a -1 in the position of each closed bracket `}` 7 | // and 0 everywhere else. 8 | // 9 | // See an example below. The real string is `arr` but we re-display it with "fake" spaces in `align_arr` 10 | // to more easily showcase which character in `arr` corresponds to the `-1` in `brackets`. 11 | // arr: {he{llo{}world!}} 12 | // align_arr: {he{llo{ }world! } } 13 | // brackets: 10010001-1000000-1-1 14 | // 15 | // where `arr` is represented by its ASCII encoding, i.e. `{` = 123 16 | template BracketsMap(LEN) { 17 | signal input arr[LEN]; 18 | signal output brackets[LEN]; 19 | 20 | for (var i = 0; i < LEN; i++) { 21 | var is_open_bracket = IsEqual()([arr[i], 123]); // 123 = `{` 22 | var is_closed_bracket = IsEqual()([arr[i], 125]); // 125 = '}' 23 | brackets[i] <== is_open_bracket + (0 - is_closed_bracket); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/wtns_utils.hpp: -------------------------------------------------------------------------------- 1 | # ifndef WTNS_UTILS_H 2 | # define WTNS_UTILS_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include "binfile_utils.hpp" 9 | 10 | namespace WtnsUtils 11 | { 12 | 13 | class Header 14 | { 15 | public: 16 | std::uint32_t n8; 17 | mpz_t prime; 18 | 19 | std::uint32_t nVars; 20 | 21 | Header() { mpz_init(prime); }; 22 | 23 | Header(Header const&) = delete; 24 | Header& operator=(Header const&) = delete; 25 | 26 | ~Header() { mpz_clear(prime); } 27 | 28 | static std::unique_ptr
29 | make_from_bin_file(BinFileUtils::BinFile& bin_file) 30 | { 31 | auto ret = std::make_unique
(); 32 | 33 | bin_file.startReadSection(1); 34 | 35 | ret->n8 = bin_file.readU32LE(); 36 | mpz_import(ret->prime, ret->n8, -1, 1, -1, 0, bin_file.read(ret->n8)); 37 | 38 | ret->nVars = bin_file.readU32LE(); 39 | 40 | bin_file.endReadSection(); 41 | 42 | return ret; 43 | } 44 | 45 | }; // class Header 46 | 47 | } // namespace WtnsUtils 48 | 49 | # endif 50 | -------------------------------------------------------------------------------- /circuit/templates/helpers/arrays/RightArraySelector.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "./SingleOneArray.circom"; 4 | 5 | // Outputs a `LEN`-bit array `out`, where: 6 | // out[i] = 0, \forall i \in [0, idx] 7 | // out[i] = 1, \forall i \in (idx, LEN) 8 | // 9 | // @preconditions 10 | // 0 <= idx < LEN 11 | // 12 | // @param LEN the length of the array 13 | // 14 | // @input idx the index after which everything is 1; assumed to be < LEN 15 | // (i.e., out[idx+1..] = 1 and out[..idx] =0) 16 | // @output out the bit array 17 | // 18 | // @notes 19 | // RightArraySelector(4)(0) -> 0111 20 | // RightArraySelector(4)(1) -> 0011 21 | // RightArraySelector(4)(2) -> 0001 22 | // RightArraySelector(4)(3) -> 0000 23 | // 24 | // TODO(Buses): Assert precondition holds via buses or tags 25 | template RightArraySelector(LEN) { 26 | signal input idx; 27 | signal output out[LEN]; 28 | 29 | // SingleOneArray is not satisfiable when idx >= LEN 30 | signal bits[LEN] <== SingleOneArray(LEN)(idx); 31 | 32 | out[0] <== 0; 33 | for (var i = 1; i < LEN; i++) { 34 | out[i] <== out[i - 1] + bits[i - 1]; 35 | } 36 | } -------------------------------------------------------------------------------- /circuit/templates/helpers/strings/IsWhitespace.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "circomlib/circuits/comparators.circom"; 4 | include "circomlib/circuits/gates.circom"; 5 | 6 | // Checks if character 'char' is a whitespace character. Returns 1 if so, 0 otherwise 7 | // Assumes char is a valid ascii character. Does not check for non-ascii unicode whitespace chars. 8 | template IsWhitespace() { 9 | signal input char; 10 | 11 | // ASCII bytes in [9, 13] are line break characters: 12 | // tab -- 9, newline -- 10, vertical tab -- 11, 13 | // form feed -- 12, carriage return -- 13 14 | signal is_line_break_part_1 <== GreaterThan(8)([char, 8]); 15 | signal is_line_break_part_2 <== LessThan(8)([char, 14]); 16 | signal is_line_break <== AND()(is_line_break_part_1, is_line_break_part_2); 17 | 18 | // 32 in ASCII is the space character 19 | signal is_space <== IsEqual()([char, 32]); 20 | 21 | // serves as a cheaper logical OR, when we know: 22 | // (1) values are either 0 or 1 and 23 | // (2) both values CANNOT be 1 at the same time 24 | signal output is_whitespace <== is_line_break + is_space; 25 | } -------------------------------------------------------------------------------- /vk-diff/README.md: -------------------------------------------------------------------------------- 1 | # VK diff tool 2 | 3 | This tool will compare a `snarkjs` Groth16 VK with the on-chain Groth16 VK. 4 | 5 | It takes as input: 6 | 1. a URL to the `snarkjs` VK 7 | 2. a network name (`devnet`, `tesnet` or `mainnet`) 8 | 9 | ...and exits with 0 if they match or 1 otherwise. 10 | It also prints the two VKs, if they differ. 11 | 12 | ## Examples 13 | 14 | Below, we run the VK diff tool against `snarkjs` VK from an old ceremony that is no longer on `devnet`. 15 | The tool correctly outputs "different": 16 | ``` 17 | cargo run -- \ 18 | -j https://raw.githubusercontent.com/aptos-labs/aptos-keyless-trusted-setup-contributions-may-2024/refs/heads/main/verification_key_39f9c44b4342ed5e6941fae36cf6c87c52b1e17f.json \ 19 | -n mainnet 20 | ``` 21 | 22 | Or, below we use the tool to match the currently-deployed VK with the latest ceremony: 23 | ``` 24 | cargo run -- \ 25 | -j https://raw.githubusercontent.com/aptos-labs/aptos-keyless-trusted-setup-contributions-jan-2025/refs/heads/main/verification_key.json \ 26 | -n mainnet 27 | ``` 28 | 29 | ## Help 30 | Run the tool with `--help` to see usage information: 31 | ``` 32 | cargo run -- --help 33 | ``` 34 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "proofServer", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/build/proverServer", 12 | "args": ["/home/jordi/circuits/tools/rollup-376-32-256-64/circuit-376-32-256-64.dat", "/home/jordi/circuits/tools/rollup-376-32-256-64/circuit-376-32-256-64_0001.zkey" ], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}/build", 15 | "environment": [], 16 | "externalConsole": false, 17 | "MIMode": "gdb", 18 | "setupCommands": [ 19 | { 20 | "description": "Enable pretty-printing for gdb", 21 | "text": "-enable-pretty-printing", 22 | "ignoreFailures": true 23 | } 24 | ] 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/alt_bn128.cpp: -------------------------------------------------------------------------------- 1 | #include "alt_bn128.hpp" 2 | 3 | namespace AltBn128 4 | { 5 | 6 | RawFq F1; 7 | F2Field F2("-1"); 8 | RawFr Fr; 9 | Curve G1(F1, "0", "3", "1", "2"); 10 | Curve> G2(F2, "0,0", 11 | "19485874751759354771024239261021720505790618469301721" 12 | "065564631296452457478373, " 13 | "26692979111999116124690738713728384254507696533290028" 14 | "8569378510910307636690", 15 | "10857046999023057135944570762232829481370756359578518" 16 | "086990519993285655852781, " 17 | "11559732032986387107991004021392285783925812861821192" 18 | "530917403151452391805634", 19 | "84956539231234314176049732474892724384181905872636001" 20 | "48770280649306958101930, " 21 | "40823678758634336813322034031454355683168513275934012" 22 | "08105741076214120093531"); 23 | 24 | Engine Engine::engine; 25 | 26 | } // namespace AltBn128 27 | -------------------------------------------------------------------------------- /scripts/rust_lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This file contains the rust lint rules that are enforced in keyless-zk-proofs. 4 | # 5 | # Note, the script assumes you have already installed cargo-sort and cargo machete: 6 | # cargo install cargo-sort 7 | # cargo install cargo-machete 8 | # 9 | # These are also installed by default when running scripts/task.sh prover-service install-deps 10 | 11 | # Make sure we're in the root of the repo. 12 | if [ ! -d ".github" ] 13 | then 14 | echo "Please run this from the root of keyless-zk-proofs!" 15 | exit 1 16 | fi 17 | 18 | # Run in check mode if requested. 19 | CHECK_ARG="" 20 | if [ "$1" = "--check" ]; then 21 | CHECK_ARG="--check" 22 | fi 23 | 24 | # Set appropriate script flags. 25 | set -e 26 | set -x 27 | 28 | # Run clippy with the keyless-zk-proofs specific configuration. 29 | cargo xclippy 30 | 31 | # Run the formatter. 32 | cargo fmt $CHECK_ARG 33 | 34 | # Once cargo-sort correctly handles workspace dependencies, 35 | # we can move to cleaner workspace dependency notation. 36 | # See: https://github.com/DevinR528/cargo-sort/issues/47 37 | cargo sort --grouped --workspace $CHECK_ARG 38 | 39 | # Check for unused rust dependencies. 40 | cargo machete 41 | -------------------------------------------------------------------------------- /circuit/templates/helpers/strings/AssertIsAsciiDigits.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "../arrays/ArraySelector.circom"; 4 | 5 | include "circomlib/circuits/bitify.circom"; 6 | include "circomlib/circuits/comparators.circom"; 7 | include "circomlib/circuits/gates.circom"; 8 | 9 | // Enforces that every value in `in` between 0 and len - 1 are valid ASCII digits: 10 | // i.e. are in [48, 57] (inclusive on both ends) 11 | template AssertIsAsciiDigits(MAX_DIGITS) { 12 | signal input in[MAX_DIGITS]; 13 | signal input len; 14 | 15 | signal selector[MAX_DIGITS] <== ArraySelector(MAX_DIGITS)(0, len); 16 | for (var i = 0; i < MAX_DIGITS; i++) { 17 | // TODO(Perf): Since numbers are in [48, 58) and 58 is less than 64 = 2^6, 18 | // we could use 6 bits below. But the Num2Bits(6) call would be applied 19 | // to elements after in[len-1], which may not necessarily be 6 bits anymore. 20 | // So, we need extra conditional logic here. 21 | _ <== Num2Bits(9)(in[i]); 22 | 23 | var is_ascii_digit = AND()( 24 | GreaterThan(9)([in[i], 47]), 25 | LessThan(9)([in[i], 58]) 26 | ); 27 | 28 | (1 - is_ascii_digit) * selector[i] === 0; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /circuit/templates/helpers/bigint/CheckCarryToZero.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | // Template originally from https://github.com/doubleblind-xyz/circom-rsa/blob/master/circuits/bigint.circom 4 | 5 | include "circomlib/circuits/bitify.circom"; 6 | include "circomlib/circuits/comparators.circom"; 7 | include "circomlib/circuits/gates.circom"; 8 | 9 | template CheckCarryToZero(N, M, K) { 10 | assert(K >= 2); 11 | 12 | var EPSILON = 3; 13 | 14 | // TODO: should use assert_bits_fit_scalar (but double check <= vs <) 15 | assert(M + EPSILON <= 253); 16 | 17 | signal input in[K]; 18 | 19 | signal carry[K]; 20 | component carryRangeChecks[K]; 21 | for (var i = 0; i < K - 1; i++){ 22 | carryRangeChecks[i] = Num2Bits(M + EPSILON - N); 23 | if (i == 0) { 24 | carry[i] <-- in[i] / (1 << N); 25 | in[i] === carry[i] * (1 << N); 26 | } else { 27 | carry[i] <-- (in[i]+carry[i - 1]) / (1 << N); 28 | in[i] + carry[i - 1] === carry[i] * (1 << N); 29 | } 30 | // checking carry is in the range of - 2^(M-N-1+eps), 2^(M+-N-1+eps) 31 | carryRangeChecks[i].in <== carry[i] + (1 << (M + EPSILON - N - 1)); 32 | } 33 | in[K - 1] + carry[K - 2] === 0; 34 | } 35 | -------------------------------------------------------------------------------- /circuit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aptos-keyless-circuit" 3 | description = "The Aptos Keyless circuit (circom) and unit tests (rust)." 4 | version = "0.1.0" 5 | 6 | # Workspace inherited keys 7 | authors = { workspace = true } 8 | edition = { workspace = true } 9 | homepage = { workspace = true } 10 | license = { workspace = true } 11 | publish = { workspace = true } 12 | repository = { workspace = true } 13 | rust-version = { workspace = true } 14 | 15 | [dependencies] 16 | anyhow = { workspace = true } 17 | aptos-crypto = { workspace = true } 18 | aptos-keyless-common = { workspace = true } 19 | aptos-logger = { workspace = true } 20 | ark-bn254 = { workspace = true } 21 | ark-ff = { workspace = true } 22 | base64 = { workspace = true } 23 | itertools = { workspace = true } 24 | num-traits = { workspace = true } 25 | rand = { workspace = true } 26 | serde_json = { workspace = true } 27 | sha2 = { workspace = true } 28 | tempfile = { workspace = true } 29 | 30 | [dev-dependencies] 31 | hex = { workspace = true } 32 | num-bigint = { workspace = true } 33 | num-modular = { version = "0.6.1", features = ["num-bigint", "num-integer", "num-traits", "std"] } 34 | rand_chacha = "0.3.1" 35 | rsa = { version = "0.9.6", features = ["sha2"] } 36 | tokio = { workspace = true } 37 | -------------------------------------------------------------------------------- /circuit/templates/helpers/rsa/FpPow65537Mod.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "./FpMul.circom"; 4 | 5 | // Template copied from https://github.com/doubleblind-xyz/circom-rsa/blob/master/circuits/rsa.circom 6 | template FpPow65537Mod(N, K) { 7 | signal input base[K]; 8 | // Exponent is hardcoded at 65537 9 | signal input modulus[K]; 10 | signal output out[K]; 11 | 12 | component doublers[16]; 13 | component adder = FpMul(N, K); 14 | for (var i = 0; i < 16; i++) { 15 | doublers[i] = FpMul(N, K); 16 | } 17 | 18 | for (var j = 0; j < K; j++) { 19 | adder.p[j] <== modulus[j]; 20 | for (var i = 0; i < 16; i++) { 21 | doublers[i].p[j] <== modulus[j]; 22 | } 23 | } 24 | for (var j = 0; j < K; j++) { 25 | doublers[0].a[j] <== base[j]; 26 | doublers[0].b[j] <== base[j]; 27 | } 28 | for (var i = 0; i + 1 < 16; i++) { 29 | for (var j = 0; j < K; j++) { 30 | doublers[i + 1].a[j] <== doublers[i].out[j]; 31 | doublers[i + 1].b[j] <== doublers[i].out[j]; 32 | } 33 | } 34 | for (var j = 0; j < K; j++) { 35 | adder.a[j] <== base[j]; 36 | adder.b[j] <== doublers[15].out[j]; 37 | } 38 | for (var j = 0; j < K; j++) { 39 | out[j] <== adder.out[j]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /circuit/templates/helpers/packing/ChunksToFieldElem.circom: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: Michael Straka, Alin Tomescu 3 | */ 4 | pragma circom 2.2.2; 5 | 6 | include "../../stdlib/functions/assert_bits_fit_scalar.circom"; 7 | 8 | // Tightly-packs many chunks into a single scalar. (Inspired by `Bits2Num` in 9 | // circomlib.) 10 | // 11 | // @param NUM_CHUNKS the number of chunks 12 | // @param BITS_PER_CHUNK the max size of each chunk in bits, such that a field 13 | // element can fit NUM_CHUNKS * BITS_PER_CHUNK bits 14 | // 15 | // @input in[NUM_CHUNKS] the chunks themselves 16 | // @output out \sum_{i = 0}^{NUM_CHUNKS} in[i] 2^{BITS_PER_CHUNK} 17 | // 18 | // TODO(Tags): `in` should be tagged with maxbits = BITS_PER_CHUNK 19 | // TODO: Rename to ChunksToScalar 20 | template ChunksToFieldElem(NUM_CHUNKS, BITS_PER_CHUNK) { 21 | // Ensure we don't exceed circom's field size here 22 | _ = assert_bits_fit_scalar(NUM_CHUNKS * BITS_PER_CHUNK); 23 | var BASE = 2**BITS_PER_CHUNK; 24 | 25 | signal input in[NUM_CHUNKS]; 26 | signal output out; 27 | 28 | var elem = in[0]; 29 | var pow = BASE; 30 | for (var i = 1; i < NUM_CHUNKS; i++) { 31 | elem += in[i] * pow; 32 | pow *= BASE; // (2^{BITS_PER_CHUNK})^i --> (2^{BITS_PER_CHUNK})^{i+1} 33 | } 34 | 35 | elem ==> out; 36 | } -------------------------------------------------------------------------------- /.github/workflows/run-tests.yaml: -------------------------------------------------------------------------------- 1 | name: run-cargo-test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | lint: 15 | name: Run linter 16 | runs-on: ubuntu-latest-16-core 17 | env: 18 | CARGO_TERM_COLOR: always 19 | steps: 20 | - uses: actions/checkout@v4 21 | with: 22 | submodules: 'recursive' 23 | - uses: pre-commit/action@v3.0.0 24 | - run: ./scripts/task.sh prover-service install-deps 25 | - run: ./scripts/rust_lint.sh --check 26 | rust: 27 | name: Run cargo test 28 | runs-on: ubuntu-latest-16-core 29 | env: 30 | CARGO_TERM_COLOR: always 31 | steps: 32 | - uses: actions/checkout@v4 33 | with: 34 | submodules: 'recursive' 35 | - run: echo "" > ~/.bashrc 36 | - run: ./scripts/task.sh setup-dev-environment 37 | - run: source ~/.bashrc && cargo test 38 | rust-macos: 39 | name: Run cargo test on macos 40 | runs-on: macos-latest-xlarge 41 | env: 42 | CARGO_TERM_COLOR: always 43 | steps: 44 | - uses: actions/checkout@v4 45 | with: 46 | submodules: 'recursive' 47 | - run: echo "" > ~/.bashrc 48 | - run: ./scripts/task.sh setup-dev-environment 49 | - run: source ~/.bashrc && cargo test 50 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/Makefile: -------------------------------------------------------------------------------- 1 | ### 2 | 3 | #Build targets 4 | host: 5 | rm -rf build_prover && mkdir build_prover && cd build_prover && \ 6 | cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../package && \ 7 | make -j$(nproc) -vvv && make install 8 | 9 | android: 10 | rm -rf build_prover_android && mkdir build_prover_android && cd build_prover_android && \ 11 | cmake .. -DTARGET_PLATFORM=ANDROID -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../package_android && \ 12 | make -j$(nproc) -vvv && make install 13 | 14 | android_x86_64: 15 | rm -rf build_prover_android_x86_64 && mkdir build_prover_android_x86_64 && cd build_prover_android_x86_64 && \ 16 | cmake .. -DTARGET_PLATFORM=ANDROID_x86_64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../package_android_x86_64 && \ 17 | make -j$(nproc) -vvv && make install 18 | 19 | ios: 20 | rm -rf build_prover_ios && mkdir build_prover_ios && cd build_prover_ios && \ 21 | cmake .. -GXcode -DTARGET_PLATFORM=IOS -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../package_ios && \ 22 | echo "" && echo "Now open Xcode and compile the generated project" && echo "" 23 | 24 | clean: 25 | rm -rf build_prover build_prover_android build_prover_android_x86_64 build_prover_ios package package_android \ 26 | package_android_x86_64 package_ios depends/gmp/package depends/gmp/package_android_arm64 \ 27 | depends/gmp/package_android_x86_64 depends/gmp/package_ios_arm64 28 | -------------------------------------------------------------------------------- /circuit/templates/helpers/arrays/LeftArraySelector.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "./SingleOneArray.circom"; 4 | 5 | // Outputs a `LEN`-bit array `out`, where: 6 | // out[i] = 1, \forall i \in [0, idx) 7 | // out[i] = 0, \forall i \in [idx, LEN) 8 | // 9 | // @preconditions 10 | // LEN > 1 11 | // 0 <= idx < LEN 12 | // 13 | // @param LEN the length of the array 14 | // 15 | // @input idx the index after which everything is 0; assumed to be < LEN 16 | // (i.e., out[idx..] = 0 and out[..idx-1] = 1) 17 | // @output out the bit array 18 | // 19 | // @notes 20 | // LeftArraySelector(4)(0) -> 0000 21 | // LeftArraySelector(4)(1) -> 1000 22 | // LeftArraySelector(4)(2) -> 1100 23 | // LeftArraySelector(4)(3) -> 1110 24 | // 25 | // TODO(Buses): Assert precondition holds via buses or tags 26 | template LeftArraySelector(LEN) { 27 | signal input idx; 28 | signal output out[LEN]; 29 | 30 | // SingleOneArray is not satisfiable when idx >= LEN 31 | signal bits[LEN] <== SingleOneArray(LEN)(idx); 32 | var sum = 0; 33 | for (var i = 0; i < LEN; i++) { 34 | sum = sum + bits[i]; 35 | } 36 | 37 | // TODO: Sum will always be 1 when idx is in bounds, which SingleOneArray enforces, 38 | // so out[LEN - 1] will always be 0. Confused. 39 | out[LEN - 1] <== 1 - sum; 40 | for (var i = LEN - 2; i >= 0; i--) { 41 | out[i] <== out[i + 1] + bits[i + 1]; 42 | } 43 | } -------------------------------------------------------------------------------- /circuit/templates/main.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "keyless.circom"; 4 | 5 | component main { public [public_inputs_hash] } = keyless( 6 | /* JWT */ 7 | 192*8, // MAX_B64U_JWT_NO_SIG_LEN 8 | 300, // MAX_B64U_JWT_HEADER_W_DOT_LEN 9 | 192*8-64, // MAX_B64U_JWT_PAYLOAD_SHA2_PADDED_LEN 10 | 11 | /* aud field */ 12 | 140, // MAX_AUD_KV_PAIR_LEN 13 | 40, // MAX_AUD_NAME_LEN 14 | 120, // MAX_AUD_VALUE_LEN 15 | 16 | /* iss field */ 17 | 140, // MAX_ISS_KV_PAIR_LEN 18 | 40, // MAX_ISS_NAME_LEN 19 | 120, // MAX_ISS_VALUE_LEN 20 | 21 | /* iat field */ 22 | 50, // MAX_IAT_KV_PAIR_LEN 23 | 10, // MAX_IAT_NAME_LEN 24 | 45, // MAX_IAT_VALUE_LEN 25 | 26 | /* nonce field */ 27 | 105, // MAX_NONCE_KV_PAIR_LEN 28 | 10, // MAX_NONCE_NAME_LEN 29 | 100, // MAX_NONCE_VALUE_LEN 30 | 31 | /* email_verified field */ 32 | 30, // MAX_EMAIL_VERIFIED_KV_PAIR_LEN 33 | 20, // MAX_EMAIL_VERIFIED_NAME_LEN 34 | 10, // MAX_EMAIL_VERIFIED_VALUE_LEN 35 | 36 | /* the user ID field (i.e., sub or email) */ 37 | 350, // MAX_UID_KV_PAIR_LEN 38 | 30, // MAX_UID_NAME_LEN 39 | 330, // MAX_UID_VALUE_LEN 40 | 41 | /* any extra field (e.g., the name field) */ 42 | 350 // MAX_EXTRA_FIELD_KV_PAIR_LEN 43 | ); 44 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/fullprover.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class FullProverImpl; 4 | 5 | enum ProverResponseType 6 | { 7 | SUCCESS, 8 | ERROR 9 | }; 10 | 11 | enum FullProverState 12 | { 13 | OK, 14 | ZKEY_FILE_LOAD_ERROR, 15 | UNSUPPORTED_ZKEY_CURVE 16 | }; 17 | 18 | enum ProverError 19 | { 20 | NONE, 21 | PROVER_NOT_READY, 22 | INVALID_INPUT, 23 | WITNESS_GENERATION_INVALID_CURVE 24 | }; 25 | 26 | struct ProverResponseMetrics 27 | { 28 | int prover_time; 29 | }; 30 | 31 | struct ProverResponse 32 | { 33 | ProverResponseType type; 34 | char const* raw_json; 35 | ProverError error; 36 | ProverResponseMetrics metrics; 37 | 38 | private: 39 | static char const* const empty_string; 40 | 41 | public: 42 | ProverResponse(ProverError _error); 43 | ProverResponse(const char* _raw_json, ProverResponseMetrics _metrics); 44 | 45 | ProverResponse() = delete; 46 | ProverResponse(ProverResponse const&) = delete; 47 | ProverResponse& operator=(ProverResponse const&) = delete; 48 | 49 | ~ProverResponse(); 50 | }; 51 | 52 | class FullProverImpl; 53 | 54 | class FullProver 55 | { 56 | FullProverImpl* impl; 57 | FullProverState state; 58 | 59 | public: 60 | FullProver() = delete; 61 | FullProver(const char* _zkeyFileName); 62 | ~FullProver(); 63 | ProverResponse prove(const char* input) const; 64 | }; 65 | -------------------------------------------------------------------------------- /circuit/benches/strings/SHA2_256.js: -------------------------------------------------------------------------------- 1 | const chai = require("chai"); 2 | const path = require("path"); 3 | const F1Field = require("ffjavascript").F1Field; 4 | const Scalar = require("ffjavascript").Scalar; 5 | // BN254's prime p, but it doesn't affect circom_tester's config; need to manually pass in the prime 6 | // exports.p = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"); 7 | //const Fr = new F1Field(exports.p); 8 | 9 | const wasm_tester = require("circom_tester").wasm; 10 | 11 | const assert = chai.assert; 12 | const expect = chai.expect; 13 | 14 | describe("SHA2-256", function () { 15 | this.timeout(100000); 16 | 17 | let CIRCOMLIB_PATH = process.env.CIRCOMLIB_PATH; 18 | let INCLUDE_PATH = path.join(__dirname, "../../templates/"); 19 | // console.log("circomlib is at:", CIRCOMLIB_PATH); 20 | 21 | it("SHA2-256 compression function constraints", async() => { 22 | 23 | const circuit = await wasm_tester( 24 | path.join(__dirname, "SHA2_256_Compression_Bench.circom"), 25 | { 26 | "prime": "bn128", 27 | "O": 2, //according to Oleksandr from iden3/circom, default was --O2 actually 28 | "include": [ INCLUDE_PATH, CIRCOMLIB_PATH ], 29 | }, 30 | ); 31 | 32 | await circuit.loadConstraints(); 33 | console.log("One SHA2-256 compression function: %d constraints, %d vars", circuit.constraints.length, circuit.nVars); 34 | console.log(); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/splitparstr.cpp: -------------------------------------------------------------------------------- 1 | #include "splitparstr.hpp" 2 | 3 | 4 | void removePars(std::string &s) { 5 | unsigned int ns = 0; 6 | while ((s.length() >=ns*2)&&(s[ns]=='(')&&(s[s.length()-1-ns] == ')')) ns++; 7 | 8 | int ins = 0; 9 | int minIns = 0; 10 | for (unsigned int i=ns; i splitParStr(std::string s) { 20 | 21 | std::vector res; 22 | std::string accS; 23 | bool scaped = false; 24 | int nPar=0; 25 | for (unsigned int i=0; i { 22 | 23 | const circuit = await wasm_tester( 24 | path.join(__dirname, "AssertIsConcatenation_Bench.circom"), 25 | { 26 | "prime": "bn128", 27 | "O": 2, //according to Oleksandr from iden3/circom, default was --O2 actually 28 | "include": [ INCLUDE_PATH, CIRCOMLIB_PATH ], 29 | }, 30 | ); 31 | 32 | await circuit.loadConstraints(); 33 | console.log("AssertIsConcatenation for JWTs: %d constraints, %d vars", circuit.constraints.length, circuit.nVars); 34 | console.log(); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /circuit/templates/helpers/arrays/SingleNegOneArray.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "../../stdlib/functions/assert_bits_fit_scalar.circom"; 4 | include "../../stdlib/functions/min_num_bits.circom"; 5 | 6 | include "circomlib/circuits/comparators.circom"; 7 | 8 | // Returns a "minus-one-hot" bit mask with a -1 at index `idx`, and 0s everywhere else. 9 | // Returns a vector of all zeros when idx >= LEN. 10 | // 11 | // @param LEN the length of the mask 12 | // 13 | // @input idx the index \in [0, LEN) where the bitmask should be 1 14 | // @output out[LEN] the "one-hot" bit mask 15 | // 16 | // @warning behaves differently than SingleOneArray: i.e., remains satisfiable even when 17 | // idx > LEN 18 | // 19 | // TODO: Rename this to make returning all 0s when out of bounds more clear 20 | template SingleNegOneArray(LEN) { 21 | signal input idx; 22 | signal output out[LEN]; 23 | signal success; 24 | 25 | var lc = 0; 26 | for (var i = 0; i < LEN; i++) { 27 | out[i] <-- (idx == i) ? -1 : 0; 28 | out[i] * (idx - i) === 0; 29 | lc = lc + out[i]; 30 | } 31 | lc ==> success; 32 | 33 | // Allows this template to return all zeros, when idx > LEN 34 | var B = min_num_bits(LEN); 35 | _ = assert_bits_fit_scalar(B); 36 | _ <== Num2Bits(B)(idx); 37 | signal idx_is_bounded <== LessThan(B)([idx, LEN]); 38 | success === -1 * idx_is_bounded; 39 | 40 | // Old equivalent code: 41 | // signal is_out_of_bounds <== GreaterEqThan(20)([idx, LEN]); 42 | // success === -1 * (1 - is_out_of_bounds); 43 | } -------------------------------------------------------------------------------- /git-hooks/compile-circom-if-needed-pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run cargo fmt in check mode 4 | cargo fmt --manifest-path circuit/Cargo.toml -- --check || { echo "Please run 'cargo fmt' in circuit/"; exit 1; } 5 | cargo fmt --manifest-path prover-service/Cargo.toml -- --check || { echo "Please run 'cargo fmt' in prover-service/"; exit 1; } 6 | cargo fmt --manifest-path keyless-common/Cargo.toml -- --check || { echo "Please run 'cargo fmt' in keyless-common/"; exit 1; } 7 | cargo fmt --manifest-path vk-diff/Cargo.toml -- --check || { echo "Please run 'cargo fmt' in vk-diff/"; exit 1; } 8 | 9 | # Check the result 10 | if [ $? -ne 0 ]; then 11 | echo "Error: 'cargo fmt' check failed in circuit/src." 12 | echo "Please run 'cargo fmt' in circuit/src and stage the changes." 13 | exit 1 14 | fi 15 | 16 | # Check if any .circom files in 'circuit/templates' changed 17 | if git diff --cached --name-only | grep -q '^circuit/templates/.*\.circom$'; then 18 | echo "Detected changes in *.circom files!" 19 | echo 20 | echo "Running circom compilation. This will take ~45 seconds..." 21 | 22 | # Move into the circuit/templates directory 23 | pushd circuit/templates > /dev/null 24 | 25 | # Run your circom command 26 | time circom --O0 -l "$(npm root -g)" main.circom --r1cs 27 | 28 | # Check if circom command succeeded 29 | if [ $? -ne 0 ]; then 30 | echo "Error: circom compilation failed. Commit aborted." 31 | popd > /dev/null 32 | exit 1 33 | fi 34 | 35 | echo "circom compilation succeeded." 36 | popd > /dev/null 37 | fi 38 | 39 | exit 0 40 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/fileloader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace BinFileUtils 15 | { 16 | 17 | class FileLoader 18 | { 19 | public: 20 | FileLoader(std::string_view const& fileName) 21 | { 22 | struct stat sb; 23 | 24 | fd = ::open(fileName.data(), O_RDONLY); 25 | if (fd == -1) 26 | { 27 | throw std::system_error(errno, std::generic_category(), "open"); 28 | } 29 | 30 | if (::fstat(fd, &sb) == -1) 31 | { /* To obtain file size */ 32 | ::close(fd); 33 | throw std::system_error(errno, std::generic_category(), "fstat"); 34 | } 35 | 36 | size = sb.st_size; 37 | 38 | auto mapped = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); 39 | 40 | if (mapped == MAP_FAILED) 41 | { 42 | ::close(fd); 43 | throw std::system_error(errno, std::generic_category(), "mmap"); 44 | } 45 | 46 | addr = reinterpret_cast(mapped); 47 | } 48 | 49 | ~FileLoader() 50 | { 51 | ::munmap(addr, size); 52 | ::close(fd); 53 | } 54 | 55 | char* dataBuffer() { return addr; } 56 | std::size_t dataSize() const { return size; } 57 | 58 | private: 59 | char* addr; 60 | std::size_t size; 61 | int fd; 62 | }; 63 | 64 | } // namespace BinFileUtils 65 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/fft.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FFT_H 2 | #define FFT_H 3 | 4 | #include "scope_guard.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | template 14 | class FFT 15 | { 16 | Field f; 17 | typedef typename Field::Element Element; 18 | 19 | std::uint32_t s; 20 | Element nqr; 21 | std::vector roots; 22 | std::vector powTwoInv; 23 | // std::uint32_t nThreads; // not used 24 | 25 | void reversePermutationInnerLoop(Element* a, std::uint64_t from, 26 | std::uint64_t to, std::uint32_t domainPow); 27 | void reversePermutation(Element* a, std::uint64_t n); 28 | void fftInnerLoop(Element* a, std::uint64_t from, std::uint64_t to, 29 | std::uint32_t s); 30 | void finalInverseInner(Element* a, std::uint64_t from, std::uint64_t to, 31 | std::uint32_t domainPow); 32 | 33 | public: 34 | FFT(std::uint64_t maxDomainSize, std::uint32_t _nThreads = 0); 35 | // ~FFT(); 36 | void fft(Element* a, std::uint64_t n); 37 | void ifft(Element* a, std::uint64_t n); 38 | 39 | std::uint32_t log2(std::uint64_t n); 40 | inline Element& root(std::uint32_t domainPow, std::uint64_t idx) 41 | { 42 | return roots[idx << (s - domainPow)]; 43 | } 44 | 45 | void printVector(Element* a, std::uint64_t n); 46 | }; 47 | 48 | // The function we want to execute on the new thread. 49 | 50 | 51 | #endif // FFT_H 52 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "buildProverServer", 8 | "type": "shell", 9 | "command": "npx task buildProverServer /home/jordi/circuits/tools/rollup-376-32-256-64/circuit-376-32-256-64.cpp", 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | }, 14 | "presentation": { 15 | "reveal": "always", 16 | "panel": "new" 17 | }, 18 | "problemMatcher": { 19 | "owner": "cpp", 20 | "fileLocation": [ 21 | "relative", 22 | "${workspaceFolder}/build" 23 | ], 24 | "pattern": [ 25 | { 26 | "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", 27 | "file": 1, 28 | "line": 2, 29 | "column": 3, 30 | "severity": 4, 31 | "message": 5 32 | } 33 | ] 34 | } 35 | }, 36 | { 37 | "label": "buildProver", 38 | "type": "shell", 39 | "command": "npx task buildProver", 40 | "group": { 41 | "kind": "build", 42 | "isDefault": true 43 | }, 44 | "presentation": { 45 | "reveal": "always", 46 | "panel": "new" 47 | }, 48 | "problemMatcher": { 49 | "owner": "cpp", 50 | "fileLocation": [ 51 | "relative", 52 | "${workspaceFolder}/build" 53 | ], 54 | "pattern": [ 55 | { 56 | "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", 57 | "file": 1, 58 | "line": 2, 59 | "column": 3, 60 | "severity": 4, 61 | "message": 5 62 | } 63 | ] 64 | } 65 | }, 66 | ] 67 | } -------------------------------------------------------------------------------- /circuit/templates/helpers/hashtofield/Hash64BitLimbsToFieldWithLen.circom: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: Michael Straka, Alin Tomescu 3 | */ 4 | pragma circom 2.2.2; 5 | 6 | include "../packing/ChunksToFieldElems.circom"; 7 | include "../packing/AssertIs64BitLimbs.circom"; 8 | 9 | include "circomlib/circuits/poseidon.circom"; 10 | 11 | // Hashes multiple 64-bit limbs to a field element using a Poseidon hash. 12 | // 13 | // We hash the length of the input as well to avoid collisions. 14 | // 15 | // Assumes `len` is the length [in bytes?] of the provided input. It is used only for hashing and is not 16 | // verified by this template. 17 | // 18 | // @param NUM_LIMBS the max number of limbs in the input array 19 | // 20 | // @input in {maxbits} an array of already-range-checked, 64-bit limbs 21 | // @input len the total # of bytes in the limbs (TODO: NUM_LIMBS*8 ==> redundant, so remove) 22 | // 23 | // @output hash a collision-resistant hash of the 64-bit limbs 24 | template Hash64BitLimbsToFieldWithLen(NUM_LIMBS) { 25 | assert(NUM_LIMBS != 0); 26 | 27 | signal input {maxbits} in[NUM_LIMBS]; 28 | signal input len; 29 | 30 | assert(in.maxbits <= 64); 31 | 32 | var NUM_ELEMS = NUM_LIMBS % 3 == 0 ? NUM_LIMBS \ 3 : NUM_LIMBS \ 3 + 1; 33 | 34 | // Pack 3 64-bit limbs per field element 35 | signal input_packed[NUM_ELEMS] <== ChunksToFieldElems(NUM_LIMBS, 3, 64)(in); 36 | 37 | signal input_with_len[NUM_ELEMS + 1]; 38 | for (var i = 0; i < NUM_ELEMS; i++) { 39 | input_with_len[i] <== input_packed[i]; 40 | } 41 | input_with_len[NUM_ELEMS] <== len; 42 | 43 | signal output hash <== Poseidon(NUM_ELEMS + 1)(input_with_len); 44 | } 45 | 46 | -------------------------------------------------------------------------------- /.github/workflows/build-image.yaml: -------------------------------------------------------------------------------- 1 | name: Build image 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - docker 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | concurrency: 13 | group: ${{ github.workflow }}-${{ github.ref }} 14 | cancel-in-progress: true 15 | 16 | permissions: 17 | contents: read 18 | id-token: write 19 | 20 | jobs: 21 | docker-image: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Checkout repo 25 | uses: actions/checkout@v4 26 | with: 27 | submodules: recursive 28 | 29 | # - name: Set up Depot CLI 30 | # uses: depot/setup-action@v1 31 | 32 | - id: auth 33 | name: "Authenticate to Google Cloud" 34 | uses: "google-github-actions/auth@v1" 35 | with: 36 | create_credentials_file: false 37 | token_format: "access_token" 38 | workload_identity_provider: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }} 39 | service_account: ${{ vars.GCP_SERVICE_ACCOUNT_EMAIL }} 40 | 41 | - name: Login to us-central1 Google Artifact Registry 42 | uses: docker/login-action@v3 43 | with: 44 | registry: us-docker.pkg.dev 45 | username: oauth2accesstoken 46 | password: ${{ steps.auth.outputs.access_token }} 47 | 48 | - name: Build and push 49 | uses: docker/build-push-action@v5 50 | with: 51 | context: . 52 | file: ./prover-service/Dockerfile 53 | push: true 54 | tags: | 55 | us-docker.pkg.dev/aptos-registry/docker/prover-service:latest 56 | us-docker.pkg.dev/aptos-registry/docker/prover-service:${{ github.sha }} 57 | -------------------------------------------------------------------------------- /circuit/templates/helpers/packing/Num2BigEndianBits.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | /** 4 | * Outputs an array of bits containing the N-bit representation of the input number, with 5 | * the most significant bit first and the least signficant last (opposite of Num2Bits). 6 | * 7 | * This effectively acts as a range check for the input number being in [0, 2^N). 8 | * 9 | * @param N correctness only holds if the number is in [0, 2^N) 10 | * soundness is unconditional, but only when 2^N - 1 "fits" in a scalar 11 | * (i.e, numbers >= 2^N cannot satisfy this template if 2^N - 1 "fits") 12 | * 13 | * @preconditions 14 | * $2^N - 1 < p$ 15 | * 16 | * @input num the number to be converted to binary 17 | * 18 | * @output bits {binary} an array bits[N-1], ..., bits[0] of the N bits representing 19 | * `num` with bits[N-1] being the least significant one 20 | * 21 | * @postconditions 22 | * $bits[i] \in \{0, 1\}$ 23 | * $num = \sum_{i = 0}^{N-1} 2^{(N-1)-i} bits[i]$ 24 | */ 25 | template Num2BigEndianBits(N) { 26 | signal input in; 27 | signal output out[N]; 28 | 29 | // incrementally-updated to eventually store the symbolic expression: 30 | // 31 | // bits[0] * 2^{N-1} + bits[1] * 2^{N-2} + bits[2] * 2^{N-3} + ... + bits[N-2] * 2^1 + bits[N-1] * 2^0 32 | // 33 | var num = 0; 34 | var pow2 = 1; 35 | 36 | for (var i = 0; i < N; i++) { 37 | var idx = (N - 1) - i; 38 | 39 | out[idx] <-- (in >> i) & 1; 40 | out[idx] * (out[idx] - 1) === 0; 41 | 42 | num += out[idx] * pow2; 43 | 44 | pow2 = pow2 + pow2; 45 | } 46 | 47 | num === in; 48 | } 49 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/f2field.hpp: -------------------------------------------------------------------------------- 1 | #ifndef F2FIELD_HPP 2 | #define F2FIELD_HPP 3 | 4 | #include "assert.h" 5 | #include "splitparstr.hpp" 6 | 7 | #include 8 | #include 9 | 10 | template 11 | class F2Field 12 | { 13 | 14 | public: 15 | struct Element 16 | { 17 | typename BaseField::Element a; 18 | typename BaseField::Element b; 19 | }; 20 | 21 | BaseField F; 22 | 23 | private: 24 | enum TypeOfNr 25 | { 26 | nr_is_zero, 27 | nr_is_one, 28 | nr_is_negone, 29 | nr_is_long 30 | }; 31 | TypeOfNr typeOfNr; 32 | 33 | typename BaseField::Element nr; 34 | 35 | Element fOne; 36 | Element fZero; 37 | Element fNegOne; 38 | 39 | void mulByNr(typename BaseField::Element& r, 40 | typename BaseField::Element& ab); 41 | 42 | void initField(typename BaseField::Element& anr); 43 | 44 | public: 45 | F2Field(typename BaseField::Element& anr); 46 | F2Field(std::string nrs); 47 | 48 | Element& zero() { return fZero; }; 49 | Element& one() { return fOne; }; 50 | Element& negOne() { return fNegOne; }; 51 | 52 | void copy(Element& r, Element& a); 53 | void add(Element& r, Element& a, Element& b); 54 | void sub(Element& r, Element& a, Element& b); 55 | void neg(Element& r, Element& a); 56 | void mul(Element& r, Element& a, Element& b); 57 | void square(Element& r, Element& a); 58 | void inv(Element& r, Element& a); 59 | void div(Element& r, Element& a, Element& b); 60 | bool isZero(Element& a); 61 | bool eq(Element& a, Element& b); 62 | 63 | void fromString(Element& r, std::string s); 64 | std::string toString(Element& a, uint32_t radix = 10); 65 | }; 66 | 67 | 68 | # endif 69 | -------------------------------------------------------------------------------- /circuit/templates/helpers/bigint/BigLessThan.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | // Template originally from https://github.com/doubleblind-xyz/circom-rsa/blob/master/circuits/bigint.circom 4 | 5 | include "circomlib/circuits/comparators.circom"; 6 | include "circomlib/circuits/gates.circom"; 7 | 8 | template BigLessThan(n, k){ 9 | signal input a[k]; 10 | signal input b[k]; 11 | signal output out; 12 | 13 | component lt[k]; 14 | component eq[k]; 15 | for (var i = 0; i < k; i++) { 16 | lt[i] = LessThan(n); 17 | lt[i].in[0] <== a[i]; 18 | lt[i].in[1] <== b[i]; 19 | eq[i] = IsEqual(); 20 | eq[i].in[0] <== a[i]; 21 | eq[i].in[1] <== b[i]; 22 | } 23 | 24 | // ors[i] holds (lt[k - 1] || (eq[k - 1] && lt[k - 2]) .. || (eq[k - 1] && .. && lt[i])) 25 | // ands[i] holds (eq[k - 1] && .. && lt[i]) 26 | // eq_ands[i] holds (eq[k - 1] && .. && eq[i]) 27 | component ors[k - 1]; 28 | component ands[k - 1]; 29 | component eq_ands[k - 1]; 30 | for (var i = k - 2; i >= 0; i--) { 31 | ands[i] = AND(); 32 | eq_ands[i] = AND(); 33 | ors[i] = OR(); 34 | 35 | if (i == k - 2) { 36 | ands[i].a <== eq[k - 1].out; 37 | ands[i].b <== lt[k - 2].out; 38 | eq_ands[i].a <== eq[k - 1].out; 39 | eq_ands[i].b <== eq[k - 2].out; 40 | ors[i].a <== lt[k - 1].out; 41 | ors[i].b <== ands[i].out; 42 | } else { 43 | ands[i].a <== eq_ands[i + 1].out; 44 | ands[i].b <== lt[i].out; 45 | eq_ands[i].a <== eq_ands[i + 1].out; 46 | eq_ands[i].b <== eq[i].out; 47 | ors[i].a <== ors[i + 1].out; 48 | ors[i].b <== ands[i].out; 49 | } 50 | } 51 | out <== ors[0].out; 52 | } -------------------------------------------------------------------------------- /circuit/templates/helpers/hashtofield/README.md: -------------------------------------------------------------------------------- 1 | # Hashing notes 2 | 3 | WARNING: Some of these algorithms are used in the keyless TXN validation logic on-chain 4 | on Aptos. Changing them haphazardly will break backwards compatibility, so exercise 5 | caution! 6 | 7 | ## Preliminaries 8 | 9 | Let $\mathbb{F}$ denote circom's finite field of prime order $p$. 10 | Let $B$ denote the number of bytes that can be fit into an element of $\mathbb{F}$ (e.g., $B = 31$ for BN254). 11 | Let $H_n : \mathbb{F}^n -> \mathbb{F}$ (e.g., Poseidon) denote a hash function family. 12 | 13 | This file implements templates for hashing various objects (byte arrays, strings, etc.), using $\mathbb{F}$ and $H_n$ as building blocks. 14 | 15 | ## Zero-padding 16 | 17 | ``` 18 | ZeroPad_{max}(b) => pb: 19 | - (b_1, ..., b_n) <- b 20 | - pb <- (b_1, ..., b_n, 0, ... , 0) s.t. |pb| = max 21 | ``` 22 | 23 | Zero-pads an array of `n` bytes `b = [b_1, ..., b_n]` up to `max` bytes. 24 | 25 | ## Packing bytes to scalar(s) 26 | 27 | ``` 28 | PackBytesToScalars_{max}(b) => (e_1, e_2, \ldots, e_k) 29 | ``` 30 | 31 | Packs $n$ bytes into $k = \lcei n/B \rceil$ field elements, zero-padding the last element 32 | when $B$ does not divide $n$. Since circom fields will typically be prime-order, even 33 | after fitting max $B$ bytes into a field element, we may be left with some extra 34 | unused *bits* at the end. This function always sets those bits to zero! 35 | 36 | WARNING: Not injective, since when there is room in a field element, we pad 37 | it with zero bytes. 38 | This is fine for our purposes, because we either hash length-suffixed byte arrays 39 | or null-terminated strings. So the non-injectiveness of this can accounted for. 40 | (Note to self: EPK *is* packed via this but its length in bytes is appended.) 41 | 42 | TODO(Docs): Continue 43 | -------------------------------------------------------------------------------- /scripts/python/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import utils 5 | from utils import eprint 6 | import prover_service 7 | import circuit 8 | import setups 9 | import setups.testing_setup 10 | import misc 11 | #from invoke import Program, Executor, Context, Collection, task 12 | import typer 13 | 14 | # Scripts now use the typer library, which automatically builds usage strings for 15 | # each command using the corresponding python function's docstring. 16 | 17 | 18 | app = typer.Typer(name="task.sh", no_args_is_help=True, rich_markup_mode="markdown") 19 | app.add_typer(prover_service.app, name="prover-service", help="Commands related to the prover service.") 20 | app.add_typer(setups.app, name="setup", help="Commands related to managing the circuit setup.") 21 | app.add_typer(circuit.app, name="circuit", help="Commands related to managing the circuit setup.") 22 | app.add_typer(misc.app, name="misc", help="Miscellaneous commands that don't fit anywhere else.") 23 | 24 | 25 | 26 | # Adding lots of space in the docstring here b/c typer needs at least two newlines in the docstring in order to print a newline... 27 | @app.command() 28 | def setup_dev_environment(): 29 | """ 30 | Installs dependencies for the prover service and circuit, and procures a testing setup. Equivalent to running: 31 | 32 | ``` 33 | 34 | ./scripts/task.sh prover-service install-deps 35 | 36 | ./scripts/task.sh prover-service add-envvars-to-profile 37 | 38 | ./scripts/task.sh circuit install-deps 39 | 40 | ./scripts/task.sh setup procure-testing-setup 41 | 42 | ``` 43 | """ 44 | prover_service.install_deps() 45 | prover_service.add_envvars_to_profile() 46 | circuit.install_deps() 47 | 48 | 49 | 50 | 51 | app(prog_name='task.sh') 52 | 53 | utils.remind_to_restart_shell_if_needed() 54 | 55 | -------------------------------------------------------------------------------- /circuit/tools/test_rsa_privkey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDpLtqxS7OrlD/d 3 | T2tuz4+QNUh2OCa2Bat4bmpY+wL3FdkqIxXUCJX0tfKpCwBikKoQMzddt+ZmoZvj 4 | zIuFv9eploqBJhoL+HYOMzuWCshACn33TZGvx9SYs3aK+vm2cvFRQ6cw5zZJC2v1 5 | 2DNM41hblm7c/DK8BaTkPq54hSEu1jOlwH562g10vcivbvjoojL9VSwPAAzt2Gup 6 | IrxTbEUIaVq7iKQ5O2/MOjCcAwcyt8TurUHpZlAMBCUGbFFCzIqWfkMiwq/rFq42 7 | wdGAEApy1TFkbwzhAkjHdLoC6CF3dFkLgJrkB7193wvyaU1gEKtCE5nt1LR/hq3h 8 | quUtxqO3AgMBAAECggEBANX6C+7EA/TADrbcCT7fMuNnMb5iGovPuiDCWc6bUIZC 9 | Q0yac45l7o1nZWzfzpOkIprJFNZoSgIF7NJmQeYTPCjAHwsSVraDYnn3Y4d1D3tM 10 | 5XjJcpX2bs1NactxMTLOWUl0JnkGwtbWp1Qq+DBnMw6ghc09lKTbHQvhxSKNL/0U 11 | C+YmCYT5ODmxzLBwkzN5RhxQZNqol/4LYVdji9bS7N/UITw5E6LGDOo/hZHWqJsE 12 | fgrJTPsuCyrYlwrNkgmV2KpRrGz5MpcRM7XHgnqVym+HyD/r9E7MEFdTLEaiiHcm 13 | Ish1usJDEJMFIWkF+rnEoJkQHbqiKlQBcoqSbCmoMWECgYEA/4379mMPF0JJ/EER 14 | 4VH7/ZYxjdyphenx2VYCWY/uzT0KbCWQF8KXckuoFrHAIP3EuFn6JNoIbja0NbhI 15 | HGrU29BZkATG8h/xjFy/zPBauxTQmM+yS2T37XtMoXNZNS/ubz2lJXMOapQQiXVR 16 | l/tzzpyWaCe9j0NT7DAU0ZFmDbECgYEA6ZbjkcOs2jwHsOwwfamFm4VpUFxYtED7 17 | 9vKzq5d7+Ii1kPKHj5fDnYkZd+mNwNZ02O6OGxh40EDML+i6nOABPg/FmXeVCya9 18 | Vump2Yqr2fAK3xm6QY5KxAjWWq2kVqmdRmICSL2Z9rBzpXmD5o06y9viOwd2bhBo 19 | 0wB02416GecCgYEA+S/ZoEa3UFazDeXlKXBn5r2tVEb2hj24NdRINkzC7h23K/z0 20 | pDZ6tlhPbtGkJodMavZRk92GmvF8h2VJ62vAYxamPmhqFW5Qei12WL+FuSZywI7F 21 | q/6oQkkYT9XKBrLWLGJPxlSKmiIGfgKHrUrjgXPutWEK1ccw7f10T2UXvgECgYEA 22 | nXqLa58G7o4gBUgGnQFnwOSdjn7jkoppFCClvp4/BtxrxA+uEsGXMKLYV75OQd6T 23 | IhkaFuxVrtiwj/APt2lRjRym9ALpqX3xkiGvz6ismR46xhQbPM0IXMc0dCeyrnZl 24 | QKkcrxucK/Lj1IBqy0kVhZB1IaSzVBqeAPrCza3AzqsCgYEAvSiEjDvGLIlqoSvK 25 | MHEVe8PBGOZYLcAdq4YiOIBgddoYyRsq5bzHtTQFgYQVK99Cnxo+PQAvzGb+dpjN 26 | /LIEAS2LuuWHGtOrZlwef8ZpCQgrtmp/phXfVi6llcZx4mMm7zYmGhh2AsA9yEQc 27 | acgc4kgDThAjD7VlXad9UHpNMO8= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/multiexp.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PAR_MULTIEXP2 2 | #define PAR_MULTIEXP2 3 | 4 | #define PME2_PACK_FACTOR 2 5 | #define PME2_MAX_CHUNK_SIZE_BITS 16 6 | #define PME2_MIN_CHUNK_SIZE_BITS 2 7 | 8 | #include "misc.hpp" 9 | #include "scope_guard.hpp" 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | template 17 | class ParallelMultiexp 18 | { 19 | 20 | struct PaddedPoint 21 | { 22 | typename Curve::Point p; 23 | // uint8_t padding[32]; 24 | }; 25 | 26 | typename Curve::PointAffine* bases; 27 | uint8_t* scalars; 28 | uint64_t scalarSize; 29 | uint64_t n; 30 | uint64_t nThreads; 31 | uint64_t bitsPerChunk; 32 | uint64_t accsPerChunk; 33 | uint64_t nChunks; 34 | Curve& g; 35 | PaddedPoint* accs; 36 | 37 | void initAccs(); 38 | 39 | uint64_t getChunk(uint64_t scalarIdx, uint64_t chunkIdx); 40 | void processChunk(uint64_t idxChunk); 41 | void processChunk(uint64_t idxChunk, uint64_t nx, uint64_t x[]); 42 | void packThreads(); 43 | void reduce(typename Curve::Point& res, uint64_t nBits); 44 | 45 | public: 46 | ParallelMultiexp(Curve& _g) 47 | : g(_g) 48 | { 49 | } 50 | void multiexp(typename Curve::Point& r, typename Curve::PointAffine* _bases, 51 | uint8_t* _scalars, uint64_t _scalarSize, uint64_t _n, 52 | uint64_t _nThreads = 0); 53 | void multiexp(typename Curve::Point& r, typename Curve::PointAffine* _bases, 54 | uint8_t* _scalars, uint64_t _scalarSize, uint64_t _n, 55 | uint64_t nx, uint64_t x[], uint64_t _nThreads = 0); 56 | }; 57 | 58 | #endif // PAR_MULTIEXP2 59 | -------------------------------------------------------------------------------- /circuit/templates/helpers/sha/SHA2_256_PaddingVerify.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "../strings/IsSubstring.circom"; 4 | include "../hashtofield/HashBytesToFieldWithLen.circom"; 5 | include "../packing/Bytes2BigEndianBits.circom"; 6 | include "../packing/BigEndianBits2Num.circom"; 7 | 8 | include "circomlib/circuits/bitify.circom"; 9 | 10 | // Verifies SHA2_256 input padding according to https://www.rfc-editor.org/rfc/rfc4634.html#section-4.1 11 | template SHA2_256_PaddingVerify(MAX_INPUT_LEN) { 12 | signal input in[MAX_INPUT_LEN]; // byte array 13 | signal input num_blocks; // Number of 512-bit blocks in `in` including sha padding 14 | signal input padding_start; // equivalent to L/8, where L is the length of the unpadded message in bits as specified in RFC4634 15 | signal input L_byte_encoded[8]; // 64-bit encoding of L 16 | signal input padding_without_len[64]; // padding_without_len[0] = 1, followed by K 0s. Length K+1, max length 512 bits. Does not include the 64-bit encoding of L 17 | 18 | var len_bits = num_blocks * 512; 19 | var padding_start_bits = padding_start * 8; 20 | var K = len_bits - padding_start_bits - 1 - 64; 21 | 22 | // Ensure K is 9-bits (i.e., < 2^9 = 512) 23 | _ <== Num2Bits(9)(K); 24 | 25 | signal in_hash <== HashBytesToFieldWithLen(MAX_INPUT_LEN)(in, num_blocks*64); 26 | // 4.1.a 27 | AssertIsSubstring(MAX_INPUT_LEN, 64)(in, in_hash, padding_without_len, (1+K)/8, padding_start); 28 | padding_without_len[0] === 128; // in binary, 1_000_0000b 29 | 30 | // 4.1.b 31 | for (var i = 1; i < 64; i++) { 32 | padding_without_len[i] === 0; 33 | } 34 | 35 | // 4.1.c 36 | AssertIsSubstring(MAX_INPUT_LEN, 8)(in, in_hash, L_byte_encoded, 8, padding_start+(K+1)/8); 37 | signal L_bits[64] <== Bytes2BigEndianBits(8)(L_byte_encoded); 38 | signal L_decoded <== BigEndianBits2Num(64)(L_bits); 39 | L_decoded === 8*padding_start; 40 | } 41 | -------------------------------------------------------------------------------- /circuit/README.md: -------------------------------------------------------------------------------- 1 | # Aptos Keyless circuit 2 | 3 | ## Installing dependencies 4 | 5 | The scripts in this repository will not work without installing the dependencies. 6 | 7 | To install, please run the following from the repo root: 8 | 9 | ``` 10 | ./scripts/task.sh circuit:install-deps 11 | ``` 12 | 13 | Optionally, you can also install a git pre-commit hook which checks that 14 | the main circuit compiles before committing, as follows: 15 | 16 | ``` 17 | ./scripts/task.sh circuit:install-deps misc:install-circom-precommit-hook 18 | ``` 19 | 20 | ## Running circuit unit tests 21 | 22 | ```bash 23 | # From the repo root: 24 | cargo test -p aptos-keyless-circuit 25 | # Or: 26 | cd circuit 27 | cargo test 28 | ``` 29 | 30 | ## Running circuit benchmarks 31 | 32 | First, do a: 33 | ``` 34 | # From the repo root 35 | npm install 36 | ``` 37 | 38 | To run all benchmarks: 39 | ``` 40 | npm test 41 | ``` 42 | 43 | To filter by benchmark names: 44 | ``` 45 | npm test -- g "your_bench_name" 46 | ``` 47 | 48 | ## Generating the proving key 49 | 50 | To generate a sample prover and verifier key pair, run the following command from the repo root: 51 | 52 | ``` 53 | ./scripts/task.sh trusted-setup:run-dummy-setup 54 | ``` 55 | 56 | ## Generating a sample proof 57 | 58 | To generate a sample proof and public input, run the following command: 59 | 60 | ``` 61 | ./tools/create-proofs-for-testing.sh 62 | ``` 63 | 64 | where `` may be generated by `tools/trusted-setup.sh` 65 | 66 | ## Circuit stats 67 | 68 | To obtain the current number of constraints and wires, run the following 69 | command in this directory: 70 | 71 | ``` 72 | circom -l `npm root -g` templates/main.circom --r1cs 73 | ``` 74 | 75 | Output: 76 | ``` 77 | non-linear constraints: 1376867 78 | linear constraints: 0 79 | public inputs: 1 80 | private inputs: 7858 (7745 belong to witness) 81 | public outputs: 0 82 | wires: 1343588 83 | labels: 6286968 84 | ``` 85 | -------------------------------------------------------------------------------- /circuit/templates/helpers/strings/AsciiDigitsToScalar.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "./AssertIsAsciiDigits.circom"; 4 | 5 | // Given a string of digits in ASCII format, returns the digits represented as a single field element 6 | // Assumes: 7 | // - the number represented by the ASCII `digits` is smaller than the scalar field used by the circuit 8 | // - `digits` contains only ASCII digit values between 48 and 57 inclusive 9 | // Does not work when MAX_LEN = 1 10 | template AsciiDigitsToScalar(MAX_LEN) { 11 | signal input digits[MAX_LEN]; 12 | signal input len; 13 | signal output out; 14 | 15 | AssertIsAsciiDigits(MAX_LEN)(digits, len); 16 | // Set to 0 everywhere except len-1, which is 1 17 | signal index_eq[MAX_LEN - 1]; 18 | 19 | // For ASCII digits ['1','2','3','4','5'], acc_shifts[0..3] is [12,123,1234] 20 | signal acc_shifts[MAX_LEN - 1]; 21 | // accumulators[i] = acc_shifts[i-1] for all i < len, otherwise accumulators[i] = accumulators[i-1] 22 | signal accumulators[MAX_LEN]; 23 | 24 | signal success; 25 | var index_eq_sum = 0; 26 | // `s` is initially set to 1 and is 0 after len == i 27 | var s = 1; 28 | 29 | accumulators[0] <== digits[0]-48; 30 | for (var i=1; i < MAX_LEN; i++) { 31 | index_eq[i-1] <-- (len == i) ? 1 : 0; 32 | index_eq[i-1] * (len-i) === 0; 33 | 34 | s = s - index_eq[i - 1]; 35 | index_eq_sum = index_eq_sum + index_eq[i - 1]; 36 | 37 | acc_shifts[i - 1] <== 10 * accumulators[i - 1] + (digits[i] - 48); 38 | // // This implements a conditional assignment: accumulators[i] = (s == 0 ? accumulators[i-1] : acc_shifts[i-1]); 39 | accumulators[i] <== (acc_shifts[i - 1] - accumulators[i - 1])*s + accumulators[i - 1]; 40 | } 41 | 42 | index_eq_sum ==> success; 43 | // Guarantee at most one element of index_eq is equal to 1 44 | success === 1; 45 | 46 | out <== accumulators[MAX_LEN - 1]; 47 | } 48 | -------------------------------------------------------------------------------- /prover-service/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1.7 2 | 3 | # The image used for building 4 | FROM debian:bookworm as build_prover_service 5 | ARG TARGETARCH 6 | 7 | # copy the entire cargo workspace into the image 8 | COPY --link . . 9 | 10 | # Install deps for building prover service 11 | RUN ./scripts/task.sh prover-service install-deps 12 | 13 | # Build prover service 14 | RUN /root/.cargo/bin/cargo build --release -p prover-service \ 15 | && cp target/release/prover-service /prover-service-bin 16 | 17 | # Download trusted setup and witness gen binaries 18 | ENV RESOURCES_DIR=/resources 19 | RUN ./scripts/task.sh setup download-ceremonies-for-releases --witness-gen-type c circuit-v1.0.1 20 | 21 | # The image that is actually deployed 22 | FROM debian:bookworm 23 | 24 | # need libyaml, gmp, and openssl dylibs to run prover service 25 | RUN apt-get update \ 26 | && apt-get install -y libyaml-dev libgmp-dev libssl-dev curl 27 | 28 | # copy prover service binary 29 | COPY --link --from=build_prover_service ./prover-service-bin ./prover-service-bin 30 | # copy oneTBB dylib 31 | COPY --link --from=build_prover_service ./rust-rapidsnark/rapidsnark/build/subprojects/oneTBB-2022.0.0 ./rapidsnark-libdir 32 | # copy trusted setup and witness generation binaries 33 | COPY --link --from=build_prover_service ./resources ./resources 34 | # copy prover service config file 35 | COPY --link ./prover-service/config.yml ./config.yml 36 | 37 | EXPOSE 8080 38 | 39 | # Add Tini to make sure the binaries receive proper SIGTERM signals when Docker is shut down 40 | # note this needs the buildx tool. On e.g. arch linux it's installed separately via the 41 | # docker-buildx package 42 | # TODO remove this after confirming with prod-eng that it is not necessary anymore 43 | ADD --chmod=755 https://github.com/krallin/tini/releases/download/v0.19.0/tini-amd64 /tini 44 | ENTRYPOINT ["/tini", "--"] 45 | 46 | ENV LD_LIBRARY_PATH="./rapidsnark-libdir" 47 | CMD ["./prover-service-bin"] 48 | -------------------------------------------------------------------------------- /prover-service/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prover-service" 3 | description = "Aptos Keyless Prover Service" 4 | version = "0.1.0" 5 | 6 | # Workspace inherited keys 7 | authors = { workspace = true } 8 | edition = { workspace = true } 9 | homepage = { workspace = true } 10 | license = { workspace = true } 11 | publish = { workspace = true } 12 | repository = { workspace = true } 13 | rust-version = { workspace = true } 14 | 15 | [dependencies] 16 | anyhow = { workspace = true } 17 | aptos-build-info = { workspace = true } 18 | aptos-crypto = { workspace = true } 19 | aptos-crypto-derive = { workspace = true } 20 | aptos-infallible = { workspace = true } 21 | aptos-keyless-common = { workspace = true } 22 | aptos-logger = { workspace = true } 23 | aptos-metrics-core = { workspace = true } 24 | aptos-time-service = { workspace = true } 25 | aptos-types = { workspace = true } 26 | ark-bn254 = { workspace = true } 27 | ark-ff = { workspace = true } 28 | ark-groth16 = { workspace = true } 29 | async-trait = { workspace = true } 30 | base64 = { workspace = true } 31 | bcs = { workspace = true } 32 | clap = { workspace = true } 33 | hex = { workspace = true } 34 | http = { workspace = true } 35 | hyper = { workspace = true } 36 | jsonwebtoken = { workspace = true } 37 | num-bigint = { workspace = true } 38 | once_cell = { workspace = true } 39 | prometheus = { workspace = true } 40 | rand = { workspace = true } 41 | regex = { workspace = true } 42 | reqwest = { workspace = true } 43 | rsa = { workspace = true } 44 | rust-rapidsnark = { workspace = true } 45 | serde = { workspace = true } 46 | serde_json = { workspace = true } 47 | serde_yaml = { workspace = true } 48 | serial_test = { workspace = true } 49 | shellexpand = { workspace = true } 50 | tempfile = { workspace = true } 51 | thiserror = { workspace = true } 52 | tokio = { workspace = true } 53 | 54 | [dev-dependencies] 55 | aptos-time-service = { workspace = true, features = ["testing"] } 56 | 57 | [package.metadata.cargo-machete] 58 | ignored = ["hex"] 59 | -------------------------------------------------------------------------------- /scripts/python/setups/ceremony_setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import utils 3 | import shutil 4 | import setups 5 | from setups.gh_release import Releases 6 | import zipfile 7 | 8 | CEREMONIES_DIR = utils.resources_dir_root() / "ceremonies" 9 | 10 | class CeremonySetup(setups.Setup): 11 | def __init__(self, release_name, repo="keyless-zk-proofs", auth_token=None): 12 | super().__init__(CEREMONIES_DIR / release_name) 13 | self.release_name = release_name 14 | self.repo = repo 15 | self.auth_token = auth_token 16 | 17 | def download(self, witness_gen_type): 18 | self.mkdir() 19 | 20 | assets = [ 21 | "prover_key.zkey", 22 | "verification_key.json", 23 | "circuit_config.yaml" 24 | ] 25 | if witness_gen_type == "c" or witness_gen_type == "both": 26 | assets += [ 27 | "wgen_c.zip" 28 | ] 29 | 30 | if witness_gen_type == "wasm" or witness_gen_type == "both": 31 | assets += [ 32 | "wgen_js.zip" 33 | ] 34 | 35 | releases = Releases(self.repo, self.auth_token) 36 | releases.download_and_install_release(self.release_name, self.path(), assets) 37 | 38 | shutil.move(self.path() / "circuit_config.yaml", self.path() / "circuit_config.yml") 39 | 40 | if witness_gen_type == "c" or witness_gen_type == "both": 41 | with zipfile.ZipFile(self.path() / 'wgen_c.zip', 'r') as zip_ref: 42 | zip_ref.extractall(self.path()) 43 | os.remove(self.path() / "wgen_c.zip") 44 | os.chmod(self.path() / "main_c", 0o744) 45 | 46 | if witness_gen_type == "wasm" or witness_gen_type == "both": 47 | with zipfile.ZipFile(self.path() / 'wgen_js.zip', 'r') as zip_ref: 48 | zip_ref.extractall(self.path()) 49 | shutil.move(self.path() / "main_c.wasm", self.path() / "main.wasm") 50 | os.remove(self.path() / "wgen_js.zip") 51 | -------------------------------------------------------------------------------- /circuit/templates/helpers/jwt/StringBodies.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "circomlib/circuits/comparators.circom"; 4 | include "circomlib/circuits/gates.circom"; 5 | 6 | // Given an array of ascii characters representing a JSON object, output a binary array demarquing 7 | // the spaces in between quotes, so that the indices in between quotes in `in` are given the value 8 | // `1` in `out`, and are 0 otherwise. Escaped quotes are not considered quotes in this subcircuit 9 | // input = { asdfsdf "as\"df" } 10 | // output = 00000000000111111000 11 | template StringBodies(LEN) { 12 | signal input in[LEN]; 13 | // TODO(Tags): Enforce binarity in a more type-safe way, rather than just declaring it here. 14 | signal output {binary} out[LEN]; 15 | 16 | 17 | signal quotes[LEN]; 18 | signal quote_parity[LEN]; 19 | signal quote_parity_1[LEN]; 20 | signal quote_parity_2[LEN]; 21 | 22 | signal backslashes[LEN]; 23 | signal adjacent_backslash_parity[LEN]; 24 | 25 | quotes[0] <== IsEqual()([in[0], 34]); 26 | quote_parity[0] <== IsEqual()([in[0], 34]); 27 | 28 | backslashes[0] <== IsEqual()([in[0], 92]); 29 | adjacent_backslash_parity[0] <== IsEqual()([in[0], 92]); 30 | 31 | for (var i = 1; i < LEN; i++) { 32 | backslashes[i] <== IsEqual()([in[i], 92]); 33 | adjacent_backslash_parity[i] <== backslashes[i] * (1 - adjacent_backslash_parity[i-1]); 34 | } 35 | 36 | for (var i = 1; i < LEN; i++) { 37 | var is_quote = IsEqual()([in[i], 34]); 38 | var prev_is_odd_backslash = adjacent_backslash_parity[i-1]; 39 | quotes[i] <== is_quote * (1 - prev_is_odd_backslash); // 1 iff there is a non-escaped quote at this position 40 | quote_parity[i] <== XOR()(quotes[i], quote_parity[i-1]); 41 | } 42 | // input = { asdfsdf "asdf" } 43 | // intermediate output = 000000000011111000 44 | // i.e., still has offset-by-one error 45 | 46 | out[0] <== 0; 47 | 48 | for (var i = 1; i < LEN; i++) { 49 | out[i] <== AND()(quote_parity[i-1], quote_parity[i]); // remove offset error 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "array": "cpp", 4 | "atomic": "cpp", 5 | "bit": "cpp", 6 | "*.tcc": "cpp", 7 | "bitset": "cpp", 8 | "cctype": "cpp", 9 | "chrono": "cpp", 10 | "clocale": "cpp", 11 | "cmath": "cpp", 12 | "condition_variable": "cpp", 13 | "cstdarg": "cpp", 14 | "cstddef": "cpp", 15 | "cstdint": "cpp", 16 | "cstdio": "cpp", 17 | "cstdlib": "cpp", 18 | "cstring": "cpp", 19 | "ctime": "cpp", 20 | "cwchar": "cpp", 21 | "cwctype": "cpp", 22 | "deque": "cpp", 23 | "forward_list": "cpp", 24 | "list": "cpp", 25 | "map": "cpp", 26 | "unordered_map": "cpp", 27 | "unordered_set": "cpp", 28 | "vector": "cpp", 29 | "exception": "cpp", 30 | "algorithm": "cpp", 31 | "buffer": "cpp", 32 | "executor": "cpp", 33 | "functional": "cpp", 34 | "internet": "cpp", 35 | "io_context": "cpp", 36 | "iterator": "cpp", 37 | "memory": "cpp", 38 | "memory_resource": "cpp", 39 | "netfwd": "cpp", 40 | "numeric": "cpp", 41 | "optional": "cpp", 42 | "random": "cpp", 43 | "ratio": "cpp", 44 | "regex": "cpp", 45 | "socket": "cpp", 46 | "string": "cpp", 47 | "string_view": "cpp", 48 | "system_error": "cpp", 49 | "timer": "cpp", 50 | "tuple": "cpp", 51 | "type_traits": "cpp", 52 | "utility": "cpp", 53 | "fstream": "cpp", 54 | "future": "cpp", 55 | "initializer_list": "cpp", 56 | "iomanip": "cpp", 57 | "iosfwd": "cpp", 58 | "iostream": "cpp", 59 | "istream": "cpp", 60 | "limits": "cpp", 61 | "mutex": "cpp", 62 | "new": "cpp", 63 | "ostream": "cpp", 64 | "shared_mutex": "cpp", 65 | "sstream": "cpp", 66 | "stdexcept": "cpp", 67 | "streambuf": "cpp", 68 | "thread": "cpp", 69 | "cinttypes": "cpp", 70 | "typeinfo": "cpp", 71 | "csignal": "cpp", 72 | "set": "cpp", 73 | "valarray": "cpp", 74 | "variant": "cpp" 75 | } 76 | } -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/alt_bn128.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ALT_BN128_HPP 2 | #define ALT_BN128_HPP 3 | 4 | #include "curve.hpp" 5 | #include "f2field.hpp" 6 | #include "fq.hpp" 7 | #include "fr.hpp" 8 | #include 9 | namespace AltBn128 10 | { 11 | 12 | typedef RawFq::Element F1Element; 13 | typedef F2Field::Element F2Element; 14 | typedef RawFr::Element FrElement; 15 | typedef Curve::Point G1Point; 16 | typedef Curve::PointAffine G1PointAffine; 17 | typedef Curve>::Point G2Point; 18 | typedef Curve>::PointAffine G2PointAffine; 19 | 20 | extern RawFq F1; 21 | extern F2Field F2; 22 | extern RawFr Fr; 23 | extern Curve G1; 24 | extern Curve> G2; 25 | 26 | class Engine 27 | { 28 | public: 29 | typedef RawFq F1; 30 | typedef F2Field F2; 31 | typedef RawFr Fr; 32 | typedef Curve G1; 33 | typedef Curve> G2; 34 | 35 | F1 f1; 36 | F2 f2; 37 | Fr fr; 38 | G1 g1; 39 | G2 g2; 40 | 41 | Engine() 42 | : f1() 43 | , f2("-1") 44 | , fr() 45 | , g1(f1, "0", "3", "1", "2") 46 | , g2(f2, "0,0", 47 | // clang-format off 48 | "19485874751759354771024239261021720505790618469301721065564631296452457478373, 266929791119991161246907387137283842545076965332900288569378510910307636690", 49 | "10857046999023057135944570762232829481370756359578518086990519993285655852781, 11559732032986387107991004021392285783925812861821192530917403151452391805634", 50 | "8495653923123431417604973247489272438418190587263600148770280649306958101930, 4082367875863433681332203403145435568316851327593401208105741076214120093531" 51 | // clang-format on 52 | ) 53 | { 54 | } 55 | 56 | typedef F1::Element F1Element; 57 | typedef F2::Element F2Element; 58 | typedef Fr::Element FrElement; 59 | typedef G1::Point G1Point; 60 | typedef G1::PointAffine G1PointAffine; 61 | typedef G2::Point G2Point; 62 | typedef G2::PointAffine G2PointAffine; 63 | 64 | static Engine engine; 65 | }; 66 | 67 | } // namespace AltBn128 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /prover-service/src/request_handler/deployment_information.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Aptos Foundation 2 | 3 | use aptos_build_info::build_information; 4 | use aptos_crypto::ed25519::Ed25519PublicKey; 5 | use aptos_infallible::Mutex; 6 | use std::{collections::BTreeMap, sync::Arc}; 7 | 8 | // The key used to store the training wheels verification key in the deployment information 9 | const TRAINING_WHEELS_VERIFICATION_KEY: &str = "training_wheels_verification_key"; 10 | 11 | /// A simple struct to hold deployment information as key-value pairs 12 | #[derive(Clone, Debug)] 13 | pub struct DeploymentInformation { 14 | deployment_information_map: Arc>>, 15 | } 16 | 17 | impl DeploymentInformation { 18 | pub fn new() -> Self { 19 | // Collect the build information and initialize the map 20 | let build_information = build_information!(); 21 | let deployment_information_map = Arc::new(Mutex::new(build_information)); 22 | 23 | Self { 24 | deployment_information_map, 25 | } 26 | } 27 | 28 | /// Adds a new key-value pair to the deployment information map 29 | pub fn extend_deployment_information(&mut self, key: String, value: String) { 30 | self.deployment_information_map.lock().insert(key, value); 31 | } 32 | 33 | /// Returns a copy of the deployment information map 34 | pub fn get_deployment_information_map(&self) -> BTreeMap { 35 | self.deployment_information_map.lock().clone() 36 | } 37 | } 38 | 39 | impl Default for DeploymentInformation { 40 | fn default() -> Self { 41 | Self::new() 42 | } 43 | } 44 | 45 | /// Creates and returns the deployment information for the prover service 46 | pub fn get_deployment_information( 47 | training_wheels_verification_key: &Ed25519PublicKey, 48 | ) -> DeploymentInformation { 49 | // Create the deployment information 50 | let mut deployment_information = DeploymentInformation::new(); 51 | 52 | // Insert the training wheels verification key into the deployment information. 53 | // This is useful for runtime verification (e.g., to ensure the correct key is being used). 54 | deployment_information.extend_deployment_information( 55 | TRAINING_WHEELS_VERIFICATION_KEY.into(), 56 | training_wheels_verification_key.to_string(), 57 | ); 58 | 59 | deployment_information 60 | } 61 | -------------------------------------------------------------------------------- /circuit/templates/helpers/rsa/RSA_PKCS1_v1_5_Verify.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | // File copied and modified from https://github.com/zkp-application/circom-rsa-verify/blob/main/circuits/rsa_verify.circom, 4 | // except we are using the `FpPow65537Mod` for exponentiation instead, as it is more efficient. 5 | 6 | include "./FpMul.circom"; 7 | include "./FpPow65537Mod.circom"; 8 | 9 | include "circomlib/circuits/bitify.circom"; 10 | 11 | // Pkcs1v15 + Sha256 12 | // exp 65537 13 | template RSA_PKCS1_v1_5_Verify(LIMB_BIT_WIDTH, NUM_LIMBS) { 14 | //signal input exp[NUM_LIMBS]; 15 | signal input sign[NUM_LIMBS]; // least-significant-limb first 16 | signal input modulus[NUM_LIMBS]; // least-significant-limb first 17 | 18 | signal input hashed[4]; // least-significant-limb first 19 | 20 | // sign ** exp mod modulus 21 | component pm = FpPow65537Mod(LIMB_BIT_WIDTH, NUM_LIMBS); 22 | for (var i = 0; i < NUM_LIMBS; i++) { 23 | pm.base[i] <== sign[i]; 24 | //pm.exp[i] <== exp[i]; 25 | pm.modulus[i] <== modulus[i]; 26 | } 27 | 28 | // 1. Check hashed data 29 | // 64 * 4 = 256 bit. the first 4 numbers 30 | for (var i = 0; i < 4; i++) { 31 | hashed[i] === pm.out[i]; 32 | } 33 | 34 | // 2. Check hash prefix and 1 byte 0x00 35 | // sha256/152 bit 36 | // 0b00110000001100010011000000001101000001100000100101100000100001100100100000000001011001010000001100000100000000100000000100000101000000000000010000100000 37 | pm.out[4] === 217300885422736416; 38 | pm.out[5] === 938447882527703397; 39 | // // remain 24 bit 40 | component num2bits_6 = Num2Bits(LIMB_BIT_WIDTH); 41 | num2bits_6.in <== pm.out[6]; 42 | var REMAINS_BITS[32] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0]; 43 | for (var i = 0; i < 32; i++) { 44 | num2bits_6.out[i] === REMAINS_BITS[31 - i]; 45 | } 46 | 47 | // 3. Check PS and em[1] = 1. the same code like golang std lib rsa.VerifyPKCS1v15 48 | for (var i = 32; i < LIMB_BIT_WIDTH; i++) { 49 | num2bits_6.out[i] === 1; 50 | } 51 | 52 | for (var i = 7; i < 31; i++) { 53 | // 0b1111111111111111111111111111111111111111111111111111111111111111 54 | pm.out[i] === 18446744073709551615; 55 | } 56 | // 0b1111111111111111111111111111111111111111111111111 57 | pm.out[31] === 562949953421311; 58 | } 59 | 60 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/naf.cpp: -------------------------------------------------------------------------------- 1 | #include "naf.hpp" 2 | 3 | static uint64_t NAFTable[1024]; 4 | 5 | bool buildNafTable() { 6 | for (int in=0; in<1024; in++) { 7 | bool carry = (in & 0x200); 8 | bool last = (in & 1); 9 | uint8_t res[8]; 10 | for (int i=0; i<8; i++) { 11 | bool cur = in & (1 << (i+1)); 12 | 13 | if (last) { 14 | if (cur) { 15 | if (carry) { 16 | last = false; carry = true; res[i] = 1; 17 | } else { 18 | last = false; carry = true; res[i] = 2; // -1 19 | } 20 | } else { 21 | if (carry) { 22 | last = false; carry = true; res[i] = 2; // -1 23 | } else { 24 | last = false; carry = false; res[i] = 1; 25 | } 26 | } 27 | } else { 28 | if (cur) { 29 | if (carry) { 30 | last = false; carry = true; res[i] = 0; 31 | } else { 32 | last = true; carry = false; res[i] = 0; 33 | } 34 | } else { 35 | if (carry) { 36 | last = true; carry = false; res[i] = 0; 37 | } else { 38 | last = false; carry = false; res[i] = 0; 39 | } 40 | } 41 | } 42 | } 43 | 44 | uint64_t r64 = (*((int64_t *)(res))); 45 | if (carry) r64 |= 0x4; 46 | if (last) r64 |= 0x8; 47 | 48 | NAFTable[in] = r64; 49 | } 50 | return true; 51 | } 52 | 53 | 54 | 55 | void buildNaf(uint8_t *r, uint8_t* scalar, unsigned int scalarSize) { 56 | int64_t *r64 = (int64_t *)r; 57 | 58 | bool carry = false; 59 | bool last = (scalar[0] & 1); 60 | int st; 61 | int64_t rs; 62 | 63 | for (unsigned int i=0; i Vec; 23 | } 24 | 25 | /// Trait for converting to a field element Fr 26 | pub trait AsFr { 27 | fn as_fr(&self) -> Fr; 28 | } 29 | 30 | /// Trait for converting from a field element Fr 31 | pub trait FromFr { 32 | fn from_fr(fr: &Fr) -> Self; 33 | } 34 | 35 | /// Trait for trying to convert from a field element Fr 36 | pub trait TryFromFr: Sized { 37 | fn try_from_fr(fr: &Fr) -> Result; 38 | } 39 | 40 | /// Trait for converting from base64 41 | pub trait FromB64 { 42 | fn from_b64(s: &str) -> Result 43 | where 44 | Self: Sized; 45 | } 46 | 47 | /// Trait for converting from hexadecimal 48 | pub trait FromHex { 49 | fn from_hex(s: &str) -> Result 50 | where 51 | Self: Sized; 52 | } 53 | 54 | impl As64BitLimbs for RSA_JWK { 55 | fn as_64bit_limbs(&self) -> Vec { 56 | let modulus_bytes = base64::decode_config(&self.n, base64::URL_SAFE_NO_PAD) 57 | .expect("JWK should always have a properly-encoded modulus"); 58 | // JWKs encode modulus in big-endian order 59 | let modulus_biguint: BigUint = BigUint::from_bytes_be(&modulus_bytes); 60 | modulus_biguint.to_u64_digits() 61 | } 62 | } 63 | 64 | impl AsFr for Pepper { 65 | fn as_fr(&self) -> Fr { 66 | Fr::from_le_bytes_mod_order(self.to_bytes()) 67 | } 68 | } 69 | 70 | impl FromHex for EphemeralPublicKey { 71 | fn from_hex(s: &str) -> Result 72 | where 73 | Self: Sized, 74 | { 75 | Ok(EphemeralPublicKey::try_from(hex::decode(s)?.as_slice())?) 76 | } 77 | } 78 | 79 | impl FromHex for Fr { 80 | fn from_hex(s: &str) -> Result 81 | where 82 | Self: Sized, 83 | { 84 | Ok(Fr::from_le_bytes_mod_order(&hex::decode(s)?)) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/zkey_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "binfile_utils.hpp" 8 | 9 | namespace ZKeyUtils 10 | { 11 | 12 | class Header 13 | { 14 | 15 | public: 16 | std::uint32_t n8q; 17 | mpz_t qPrime; 18 | std::uint32_t n8r; 19 | mpz_t rPrime; 20 | 21 | std::uint32_t nVars; 22 | std::uint32_t nPublic; 23 | std::uint32_t domainSize; 24 | uint64_t nCoefs; 25 | 26 | void* vk_alpha1; 27 | void* vk_beta1; 28 | void* vk_beta2; 29 | void* vk_gamma2; 30 | void* vk_delta1; 31 | void* vk_delta2; 32 | 33 | Header() 34 | { 35 | mpz_init(qPrime); 36 | mpz_init(rPrime); 37 | } 38 | 39 | ~Header() 40 | { 41 | mpz_clear(qPrime); 42 | mpz_clear(rPrime); 43 | } 44 | 45 | Header(Header const&) = delete; // no copy constructor 46 | Header& operator=(Header const&) = delete; 47 | 48 | static std::unique_ptr
49 | make_from_bin_file(BinFileUtils::BinFile& bin_file) 50 | { 51 | // A memory leak was here 52 | auto ret = std::make_unique
(); 53 | 54 | bin_file.startReadSection(1); 55 | uint32_t protocol = bin_file.readU32LE(); 56 | if (protocol != 1) 57 | { 58 | throw std::invalid_argument("zkey file is not groth16"); 59 | } 60 | bin_file.endReadSection(); 61 | 62 | bin_file.startReadSection(2); 63 | 64 | ret->n8q = bin_file.readU32LE(); 65 | mpz_import(ret->qPrime, ret->n8q, -1, 1, -1, 0, 66 | bin_file.read(ret->n8q)); 67 | 68 | ret->n8r = bin_file.readU32LE(); 69 | mpz_import(ret->rPrime, ret->n8r, -1, 1, -1, 0, 70 | bin_file.read(ret->n8r)); 71 | 72 | ret->nVars = bin_file.readU32LE(); 73 | ret->nPublic = bin_file.readU32LE(); 74 | ret->domainSize = bin_file.readU32LE(); 75 | 76 | ret->vk_alpha1 = bin_file.read(ret->n8q * 2); 77 | ret->vk_beta1 = bin_file.read(ret->n8q * 2); 78 | ret->vk_beta2 = bin_file.read(ret->n8q * 4); 79 | ret->vk_gamma2 = bin_file.read(ret->n8q * 4); 80 | ret->vk_delta1 = bin_file.read(ret->n8q * 2); 81 | ret->vk_delta2 = bin_file.read(ret->n8q * 4); 82 | bin_file.endReadSection(); 83 | 84 | ret->nCoefs = bin_file.getSectionSize(4) / (12 + ret->n8r); 85 | 86 | return ret; 87 | } 88 | 89 | }; // class Header 90 | 91 | } // namespace ZKeyUtils 92 | -------------------------------------------------------------------------------- /circuit/templates/helpers/packing/ChunksToFieldElems.circom: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: Michael Straka, Alin Tomescu 3 | */ 4 | pragma circom 2.2.2; 5 | 6 | include "ChunksToFieldElem.circom"; 7 | 8 | // Tightly-packs many chunks into many scalars. 9 | // 10 | // @param NUM_CHUNKS the number of chunks to pack; cannot be 0 11 | // @param BITS_PER_CHUNK the max size of each chunk in bits 12 | // @param CHUNKS_PER_SCALAR a scalar can fit at most 13 | // CHUNKS_PER_SCALAR * BITS_PER_CHUNK bits 14 | // 15 | // @input in the chunks to be packed 16 | // @output out[NUM_SCALARS] array of NUM_SCALARS scalars packing the chunks, where 17 | // NUM_SCALARS = ceil(NUM_CHUNKS / CHUNKS_PER_SCALAR) 18 | // 19 | // TODO: Rename to ChunksToScalars 20 | template ChunksToFieldElems(NUM_CHUNKS, CHUNKS_PER_SCALAR, BITS_PER_CHUNK) { 21 | assert(NUM_CHUNKS != 0); 22 | 23 | var NUM_CHUNKS_IN_LAST_SCALAR; 24 | var NUM_SCALARS; 25 | 26 | if (NUM_CHUNKS % CHUNKS_PER_SCALAR == 0) { 27 | // The chunks can be spread evenly across the scalars 28 | NUM_CHUNKS_IN_LAST_SCALAR = CHUNKS_PER_SCALAR; 29 | NUM_SCALARS = NUM_CHUNKS \ CHUNKS_PER_SCALAR; 30 | } else { 31 | // The chunks CANNOT be spread evenly across the scalars 32 | // => the last scalar will have < CHUNKS_PER_SCALAR chunks 33 | NUM_CHUNKS_IN_LAST_SCALAR = NUM_CHUNKS % CHUNKS_PER_SCALAR; // in [0, CHUNKS_PER_SCALAR) 34 | NUM_SCALARS = 1 + NUM_CHUNKS \ CHUNKS_PER_SCALAR; 35 | } 36 | 37 | signal input in[NUM_CHUNKS]; 38 | signal output out[NUM_SCALARS]; 39 | 40 | component chunksToScalar[NUM_SCALARS]; 41 | for (var i = 0; i < NUM_SCALARS - 1; i++) { 42 | chunksToScalar[i] = ChunksToFieldElem(CHUNKS_PER_SCALAR, BITS_PER_CHUNK); 43 | } 44 | 45 | chunksToScalar[NUM_SCALARS - 1] = ChunksToFieldElem(NUM_CHUNKS_IN_LAST_SCALAR, BITS_PER_CHUNK); 46 | 47 | // Assign all but the last field element 48 | for (var i = 0; i < NUM_SCALARS - 1; i++) { 49 | for (var j = 0; j < CHUNKS_PER_SCALAR; j++) { 50 | var IDX = (i * CHUNKS_PER_SCALAR) + j; 51 | chunksToScalar[i].in[j] <== in[IDX]; 52 | } 53 | chunksToScalar[i].out ==> out[i]; 54 | } 55 | 56 | // Assign the last field element 57 | for (var j = 0; j < NUM_CHUNKS_IN_LAST_SCALAR; j++) { 58 | var IDX = (NUM_SCALARS - 1) * CHUNKS_PER_SCALAR + j; 59 | chunksToScalar[NUM_SCALARS - 1].in[j] <== in[IDX]; 60 | } 61 | chunksToScalar[NUM_SCALARS - 1].out ==> out[NUM_SCALARS - 1]; 62 | } 63 | -------------------------------------------------------------------------------- /prover-service/src/tests/utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Aptos Foundation 2 | 3 | use crate::tests::types::{DefaultTestJWKKeyPair, TestJWKKeyPair}; 4 | use num_bigint::BigUint; 5 | use rsa::pkcs1::{EncodeRsaPrivateKey, LineEnding}; 6 | use rsa::rand_core; 7 | use rsa::traits::PublicKeyParts; 8 | use std::str::FromStr; 9 | 10 | /// A simple RSA public key representation (for testing) 11 | #[derive(Debug, PartialEq, Eq)] 12 | pub struct RsaPublicKey { 13 | modulus: BigUint, 14 | } 15 | 16 | impl RsaPublicKey { 17 | /// Returns the modulus as a base64 string 18 | pub fn as_mod_b64(&self) -> String { 19 | base64::encode_config(self.modulus.to_bytes_be(), base64::URL_SAFE_NO_PAD) 20 | } 21 | } 22 | 23 | /// A simple RSA private key representation (for testing) 24 | pub struct RsaPrivateKey { 25 | internal_private_key: rsa::RsaPrivateKey, 26 | } 27 | 28 | impl RsaPrivateKey { 29 | /// Generates a new RSA private key with the given bit size and public exponent 30 | pub fn new_with_exp( 31 | rng: &mut R, 32 | bit_size: usize, 33 | exp: &BigUint, 34 | ) -> anyhow::Result 35 | where 36 | R: rand_core::CryptoRngCore + ?Sized, 37 | { 38 | let exp_rsa_type = rsa::BigUint::from_bytes_be(&exp.to_bytes_be()); 39 | let internal_private_key = rsa::RsaPrivateKey::new_with_exp(rng, bit_size, &exp_rsa_type)?; 40 | Ok(Self { 41 | internal_private_key, 42 | }) 43 | } 44 | 45 | /// Returns the encoding key for this private key (for JWT signing) 46 | pub fn as_encoding_key(&self) -> jsonwebtoken::EncodingKey { 47 | let pkcs1_pem = self 48 | .internal_private_key 49 | .to_pkcs1_pem(LineEnding::LF) 50 | .unwrap(); 51 | jsonwebtoken::EncodingKey::from_rsa_pem(pkcs1_pem.as_bytes()).unwrap() 52 | } 53 | } 54 | 55 | impl From<&RsaPrivateKey> for RsaPublicKey { 56 | fn from(value: &RsaPrivateKey) -> Self { 57 | RsaPublicKey { 58 | modulus: BigUint::from_bytes_be(&value.internal_private_key.n().to_bytes_be()), 59 | } 60 | } 61 | } 62 | 63 | /// Generates a test JWK keypair 64 | pub fn generate_test_jwk_keypair() -> impl TestJWKKeyPair { 65 | generate_test_jwk_keypair_with_kid("test-rsa") 66 | } 67 | 68 | /// Generates a test JWK keypair with the given KID 69 | pub fn generate_test_jwk_keypair_with_kid(kid: &str) -> impl TestJWKKeyPair { 70 | let exp = BigUint::from_str("65537").unwrap(); 71 | let mut rng = rand_core::OsRng; 72 | 73 | DefaultTestJWKKeyPair::new_with_kid_and_exp(&mut rng, kid, exp).unwrap() 74 | } 75 | -------------------------------------------------------------------------------- /circuit/templates/helpers/hashtofield/HashBytesToFieldWithLen.circom: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: Michael Straka, Alin Tomescu 3 | */ 4 | pragma circom 2.2.2; 5 | 6 | include "../packing/AssertIsBytes.circom"; 7 | include "../packing/ChunksToFieldElems.circom"; 8 | 9 | include "PoseidonBN254Hash.circom"; 10 | include "HashElemsToField.circom"; 11 | 12 | /** 13 | * Hashes multiple bytes to one field element using Poseidon. 14 | * We hash the length `len` of the input as well to prevent collisions. 15 | * 16 | * Currently, does not work for inputs larger than $64 \times 31 = 1984$ bytes. 17 | * TODO(Comment): Why? 18 | * 19 | * TODO(Buses): If `in` is `Bytes(MAX_LEN)` bus, then we can remove the `AssertIsBytes` 20 | * constraint here, since it may be unnecessarily repeated if this gets called for the 21 | * same byte sub-sequence repeatedly. 22 | * 23 | * Parameters: 24 | * NUM_BYTES the max number of bytes this can handle; is > 0 and <= 1984 (64 * 31) 25 | * 26 | * Input signals: 27 | * in[NUM_BYTES] array to be hashed, although only in[0], in[1], ..., in[len-1]; 28 | * constrained to ensure elements are actually bytes 29 | * are actually hashed 30 | * len the number of bytes that will be actually hashed; 31 | * bytes `in[len], in[len+1]..., in[NUM_BYTES-1]` are ignored 32 | * 33 | * Output signals: 34 | * hash the Poseidon-BN254 hash of these bytes 35 | * 36 | * Notes: 37 | * There is no way to meaningfully ensure that `len` is the actual length of the bytes in `in`. 38 | * TODO(Buses): Some type-safety via a `Bytes(MAX_LEN)` bus may be useful here? 39 | */ 40 | template HashBytesToFieldWithLen(NUM_BYTES) { 41 | assert(NUM_BYTES > 0); 42 | signal input in[NUM_BYTES]; 43 | signal input len; 44 | signal output hash; 45 | 46 | AssertIsBytes(NUM_BYTES)(in); 47 | 48 | var NUM_ELEMS = NUM_BYTES % 31 == 0 ? NUM_BYTES\31 : NUM_BYTES\31 + 1; 49 | 50 | // Pack 31 bytes per field element 51 | signal input_packed[NUM_ELEMS] <== ChunksToFieldElems( 52 | NUM_BYTES, // inputLen (i.e., max input len) 53 | 31, // chunksPerFieldElem 54 | 8 // bitsPerChunk 55 | )(in); 56 | 57 | // TODO(Cleanup): Can't we use a var here? We are simply re-assigning signals, it seems. 58 | signal input_with_len[NUM_ELEMS + 1]; 59 | for (var i = 0; i < NUM_ELEMS; i++) { 60 | input_with_len[i] <== input_packed[i]; 61 | } 62 | input_with_len[NUM_ELEMS] <== len; 63 | 64 | PoseidonBN254Hash() poseidonHash <== HashElemsToField(NUM_ELEMS + 1)(input_with_len); 65 | 66 | hash <== poseidonHash.value; 67 | } 68 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/splitparstr_test.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "splitparstr.hpp" 3 | 4 | namespace { 5 | 6 | TEST(splitParStr, SplitIn2) { 7 | auto v = splitParStr("123,456"); 8 | 9 | ASSERT_EQ(v.size(), 2); 10 | ASSERT_STREQ(v[0].c_str(), "123"); 11 | ASSERT_STREQ(v[1].c_str(), "456"); 12 | } 13 | 14 | TEST(splitParStr, SplitIn3) { 15 | auto v = splitParStr("123,456,789"); 16 | 17 | ASSERT_EQ(v.size(), 3); 18 | ASSERT_STREQ(v[0].c_str(), "123"); 19 | ASSERT_STREQ(v[1].c_str(), "456"); 20 | ASSERT_STREQ(v[2].c_str(), "789"); 21 | } 22 | 23 | TEST(splitParStr, SplitIn2InParenthesis) { 24 | auto v = splitParStr("(123,456)"); 25 | 26 | ASSERT_EQ(v.size(), 2); 27 | ASSERT_STREQ(v[0].c_str(), "123"); 28 | ASSERT_STREQ(v[1].c_str(), "456"); 29 | } 30 | 31 | TEST(splitParStr, SplitIn2InManyParenthesis) { 32 | auto v = splitParStr("(((123,456),(789,abc)))"); 33 | /* 34 | for (int i=0; i out = [0, 1, 6] 23 | template BigEndianBitsToScalars(NUM_BITS, BITS_PER_SCALAR) { 24 | _ = assert_bits_fit_scalar(BITS_PER_SCALAR); 25 | 26 | var NUM_SCALARS; 27 | var NUM_BITS_IN_LAST_SCALAR; 28 | if (NUM_BITS % BITS_PER_SCALAR == 0) { 29 | NUM_BITS_IN_LAST_SCALAR = BITS_PER_SCALAR; // The last field element is full 30 | NUM_SCALARS = NUM_BITS \ BITS_PER_SCALAR; 31 | } else { 32 | NUM_BITS_IN_LAST_SCALAR = NUM_BITS % BITS_PER_SCALAR; 33 | NUM_SCALARS = 1 + (NUM_BITS \ BITS_PER_SCALAR); 34 | } 35 | 36 | signal input in[NUM_BITS]; 37 | signal output elems[NUM_SCALARS]; 38 | 39 | component beBits2Num[NUM_SCALARS]; 40 | for (var i = 0; i < NUM_SCALARS - 1; i++) { 41 | beBits2Num[i] = BigEndianBits2Num(BITS_PER_SCALAR); // assign circuit component 42 | } 43 | 44 | // Assign all but the last field element 45 | for (var i = 0; i < NUM_SCALARS - 1; i++) { 46 | for (var j = 0; j < BITS_PER_SCALAR; j++) { 47 | var index = (i * BITS_PER_SCALAR) + j; 48 | beBits2Num[i].in[j] <== in[index]; 49 | } 50 | beBits2Num[i].out ==> elems[i]; 51 | } 52 | 53 | // Assign the last field element 54 | beBits2Num[NUM_SCALARS - 1] = BigEndianBits2Num(NUM_BITS_IN_LAST_SCALAR); 55 | for (var j = 0; j < NUM_BITS_IN_LAST_SCALAR; j++) { 56 | var index = ((NUM_SCALARS - 1) * BITS_PER_SCALAR) + j; 57 | beBits2Num[NUM_SCALARS - 1].in[j] <== in[index]; 58 | } 59 | beBits2Num[NUM_SCALARS - 1].out ==> elems[NUM_SCALARS - 1]; 60 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Keyless ZK Circuit and ZK Proving Service 2 | 3 | This repo contains: 4 | 1. The `circom` implementation of the Aptos Keyless ZK relation from AIP-61 in `circuit/templates/`. 5 | 2. An implementation of a ZK proving service in `prover-service/`. 6 | 3. A circom unit testing framework in `circuit/`. Its 7 | [README](./circuit/README.md) contains instructions for running the 8 | circuit unit tests. 9 | 4. Some shared rust code in `keyless-common/`. 10 | 5. A VK diff tool in `vk-diff` (see its [README](/vk-diff) for details). 11 | 12 | ## Development environment setup 13 | 14 | To setup your environment for both the prover service and the circuit, run 15 | the following command: 16 | 17 | ``` 18 | ./scripts/task.sh setup-dev-environment 19 | ``` 20 | 21 | Optionally, it is possible to install a precommit hook that checks whether 22 | the circuit compiles before committing. To do this, run the following 23 | command: 24 | 25 | ``` 26 | ./scripts/task.sh misc install-circom-precommit-hook 27 | ``` 28 | 29 | For more information on the actions defined for this repo, see [the scripts 30 | README](./scripts/README.md). 31 | 32 | ## Testing the prover service and circuit 33 | 34 | The prover service already contains unit tests that verify prover request handling 35 | and proof generation. Internally, these tests procure an untrusted setup corresponding 36 | to the current circuit in this repository. For example, the unit tests will invoke the 37 | following command before running the tests: 38 | ``` 39 | ./scripts/task.sh setup procure-testing-setup 40 | ``` 41 | 42 | ### Caching the testing setup 43 | 44 | To avoid procuring the testing setup every time the tests are run, the setup will be cached 45 | locally, and (optionally) uploaded to Google cloud via the gcloud CLI. 46 | 47 | To clear the local testing setup cache, remove the setups in the local testing directory, e.g., 48 | ``` 49 | ~/.local/share/aptos-keyless 50 | ``` 51 | 52 | ## Running the prover service locally 53 | 54 | ### Start the prover service 55 | Ensure you have already completed the [development environment setup](#development-environment-setup) step, 56 | and run the following command from a new terminal (with the working directory being the repo root): 57 | ``` 58 | ./scripts/run_prover_service.sh 59 | ``` 60 | 61 | ### Interact with the prover service 62 | Next, login to [Aptos Connect](https://aptosconnect.app/), and find a real prover request payload as outlined below: 63 | 1. Open browser developer tools (F12). 64 | 2. Navigate to Network Tab. 65 | 3. Select a request with name `prove`. 66 | 4. Go to its `Payload` detail page. 67 | 68 | Save the payload as `/tmp/prover_request_payload.json`. 69 | 70 | In a new terminal, make a request to the prover and expect it to finish normally. 71 | ```bash 72 | curl -X POST -H "Content-Type: application/json" -d @/tmp/prover_request_payload.json http://localhost:8083/v0/prove 73 | ``` 74 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # What is the change being pushed? 2 | 3 | 9 | 10 | ## Why are you pushing this change? 11 | 12 | 20 | 21 | ## How is this implemented? 22 | 23 | 33 | 34 | # Type of change 35 | 36 | 41 | 42 | **Circuit change?** 43 | 44 | - [ ] New circuit feature 45 | - [ ] Circuit correctness fix 46 | - [ ] Circuit soundness fix 47 | - [ ] Circuit test cases 48 | - [ ] Circuit benchmarks 49 | 50 | **Prover service change?** 51 | 52 | - [ ] New feature 53 | - [ ] Bug fix 54 | - [ ] Breaking change 55 | - [ ] Performance improvement 56 | - [ ] Refactoring 57 | - [ ] Dependency update 58 | - [ ] Documentation update 59 | - [ ] Tests 60 | - [ ] Benchmarks 61 | 62 | # Checklist 63 | 64 | - [ ] I have performed a self-review of my own code 65 | - [ ] I have commented my code, particularly in hard-to-understand areas 66 | - [ ] I identified and added all keyless stakeholders and component owners affected by this change as reviewers 67 | - [ ] I tested both happy and unhappy path of the functionality 68 | - [ ] I have made corresponding changes to the documentation 69 | 70 | 71 | -------------------------------------------------------------------------------- /circuit/templates/helpers/jwt/EmailVerifiedCheck.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "../../stdlib/circuits/ConditionallyAssertEqual.circom"; 4 | 5 | include "circomlib/circuits/comparators.circom"; 6 | include "circomlib/circuits/gates.circom"; 7 | 8 | // Enforce that if uid name is "email", the email verified field is either true or "true" 9 | template EmailVerifiedCheck(MAX_EV_NAME_LEN, MAX_EV_VALUE_LEN, MAX_UID_NAME_LEN) { 10 | signal input ev_name[MAX_EV_NAME_LEN]; 11 | signal input ev_value[MAX_EV_VALUE_LEN]; 12 | signal input ev_value_len; 13 | signal input uid_name[MAX_UID_NAME_LEN]; 14 | signal input uid_name_len; 15 | signal output {binary} uid_is_email; 16 | 17 | var EMAIL[5] = [101, 109, 97, 105, 108]; // email 18 | 19 | var uid_starts_with_email_0 = IsEqual()([EMAIL[0], uid_name[0]]); 20 | var uid_starts_with_email_1 = IsEqual()([EMAIL[1], uid_name[1]]); 21 | var uid_starts_with_email_2 = IsEqual()([EMAIL[2], uid_name[2]]); 22 | var uid_starts_with_email_3 = IsEqual()([EMAIL[3], uid_name[3]]); 23 | var uid_starts_with_email_4 = IsEqual()([EMAIL[4], uid_name[4]]); 24 | 25 | var uid_starts_with_email = MultiAND(5)([uid_starts_with_email_0, uid_starts_with_email_1, uid_starts_with_email_2, uid_starts_with_email_3, uid_starts_with_email_4]); 26 | 27 | 28 | signal uid_name_len_is_5 <== IsEqual()([uid_name_len, 5]); 29 | uid_is_email <== AND()(uid_starts_with_email, uid_name_len_is_5); // '1' if uid_name is "email" with length 5. This guarantees uid_name is in fact "email" (with quotes) combined with the logic in `JWTFieldCheck` 30 | 31 | var required_ev_name[14] = [101, 109, 97, 105, 108, 95, 118, 101, 114, 105, 102, 105, 101, 100]; // email_verified 32 | 33 | // If uid name is "email", enforce ev_name is "email_verified" 34 | for (var i = 0; i < 14; i++) { 35 | ConditionallyAssertEqual()([ev_name[i], required_ev_name[i]], uid_is_email); 36 | } 37 | 38 | signal ev_val_len_is_4 <== IsEqual()([ev_value_len, 4]); 39 | signal ev_val_len_is_6 <== IsEqual()([ev_value_len, 6]); 40 | var ev_val_len_is_correct = OR()(ev_val_len_is_4, ev_val_len_is_6); 41 | 42 | signal not_uid_is_email <== NOT()(uid_is_email); 43 | signal is_ok <== OR()(not_uid_is_email, ev_val_len_is_correct); 44 | is_ok === 1; 45 | 46 | var required_ev_val_len_4[4] = [116, 114, 117, 101]; // true 47 | signal {binary} check_ev_val_bool <== AND()(ev_val_len_is_4, uid_is_email); 48 | for (var i = 0; i < 4; i ++) { 49 | ConditionallyAssertEqual()([required_ev_val_len_4[i], ev_value[i]], check_ev_val_bool); 50 | } 51 | 52 | var required_ev_val_len_6[6] = [34, 116, 114, 117, 101, 34]; // "true" 53 | signal {binary} check_ev_val_str <== AND()(ev_val_len_is_6, uid_is_email); 54 | for (var i = 0; i < 6; i++) { 55 | ConditionallyAssertEqual()([required_ev_val_len_6[i], ev_value[i]], check_ev_val_str); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /keyless-common/src/types.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Aptos Foundation 2 | 3 | use crate::input_processing::encoding::{AsFr, FromFr, TryFromFr}; 4 | use anyhow::anyhow; 5 | use ark_bn254::Fr; 6 | use ark_ff::{BigInteger, PrimeField}; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | // A type alias for ephemeral public key blinders 10 | pub type EphemeralPublicKeyBlinder = Vec; 11 | 12 | impl AsFr for EphemeralPublicKeyBlinder { 13 | fn as_fr(&self) -> Fr { 14 | Fr::from_le_bytes_mod_order(self) 15 | } 16 | } 17 | 18 | impl FromFr for EphemeralPublicKeyBlinder { 19 | fn from_fr(fr: &Fr) -> Self { 20 | fr.into_bigint().to_bytes_le() 21 | } 22 | } 23 | 24 | // A type alias for Poseidon hash outputs 25 | pub type PoseidonHash = [u8; 32]; 26 | 27 | impl AsFr for PoseidonHash { 28 | fn as_fr(&self) -> Fr { 29 | Fr::from_le_bytes_mod_order(self.as_slice()) 30 | } 31 | } 32 | 33 | impl TryFromFr for PoseidonHash { 34 | fn try_from_fr(fr: &Fr) -> anyhow::Result { 35 | let v = fr.into_bigint().to_bytes_le(); 36 | let arr: PoseidonHash = v 37 | .try_into() 38 | .map_err(|_| anyhow!("Conversion from Fr to bytes failed!"))?; 39 | Ok(arr) 40 | } 41 | } 42 | 43 | /// This struct is a representation of a Groth16VerificationKey resource as found on-chain. 44 | /// See, for example: 45 | /// https://fullnode.testnet.aptoslabs.com/v1/accounts/0x1/resource/0x1::keyless_account::Groth16VerificationKey 46 | /// 47 | /// Example JSON: 48 | /// { 49 | /// "type": "0x1::keyless_account::Groth16VerificationKey", 50 | /// "data": { 51 | /// "alpha_g1": "0xe2f26dbea299f5223b646cb1fb33eadb059d9407559d7441dfd902e3a79a4d2d", 52 | /// "beta_g2": "0xabb73dc17fbc13021e2471e0c08bd67d8401f52b73d6d07483794cad4778180e0c06f33bbc4c79a9cadef253a68084d382f17788f885c9afd176f7cb2f036789", 53 | /// "delta_g2": "0xb106619932d0ef372c46909a2492e246d5de739aa140e27f2c71c0470662f125219049cfe15e4d140d7e4bb911284aad1cad19880efb86f2d9dd4b1bb344ef8f", 54 | /// "gamma_abc_g1": [ 55 | /// "0x6123b6fea40de2a7e3595f9c35210da8a45a7e8c2f7da9eb4548e9210cfea81a", 56 | /// "0x32a9b8347c512483812ee922dc75952842f8f3083edb6fe8d5c3c07e1340b683" 57 | /// ], 58 | /// "gamma_g2": "0xedf692d95cbdde46ddda5ef7d422436779445c5e66006a42761e1f12efde0018c212f3aeb785e49712e7a9353349aaf1255dfb31b7bf60723a480d9293938e19" 59 | /// } 60 | /// } 61 | #[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)] 62 | pub struct OnChainGroth16VerificationKey { 63 | pub r#type: String, // Note: "type" is a reserved keyword, so we use raw identifier syntax 64 | pub data: VKeyData, 65 | } 66 | 67 | /// Data portion of the on-chain Groth16 VK 68 | #[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)] 69 | pub struct VKeyData { 70 | pub alpha_g1: String, 71 | pub beta_g2: String, 72 | pub delta_g2: String, 73 | pub gamma_abc_g1: Vec, 74 | pub gamma_g2: String, 75 | } 76 | -------------------------------------------------------------------------------- /scripts/python/setups/gh_release.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import utils 3 | from datetime import datetime 4 | 5 | class ReleaseNotFound(Exception): 6 | def __init__(self, release_name): 7 | super().__init__("Release \"" + release_name + "\" not found.") 8 | self.release_name = release_name 9 | 10 | class ReleaseMissingRequiredAsset(Exception): 11 | def __init__(self, release_name, required_asset): 12 | super().__init__("Release \"" + release_name + "\" is missing required asset \"" + required_asset + "\".") 13 | self.release_name = release_name 14 | self.required_asset = required_asset 15 | 16 | 17 | 18 | class Releases: 19 | 20 | def __init__(self, repo='keyless-zk-proofs', auth_token=None): 21 | self.auth_token = auth_token 22 | self.data = utils.read_json_from_url(f"https://api.github.com/repos/aptos-labs/{repo}/releases", auth_token) 23 | # Convert the 'created_at' field to a datetime so that we can 24 | # sort based on it 25 | for release in self.data: 26 | release['created_at'] = \ 27 | datetime.fromisoformat(release['created_at']) 28 | # Sort based on release creation time 29 | self.data.sort(key=lambda release: release['created_at']) 30 | 31 | def release_names(self): 32 | return [ release['tag_name'] for release in self.data ] 33 | 34 | def release_with_name(self, release_name): 35 | for release in self.data: 36 | if release['tag_name'] == release_name: 37 | return release 38 | 39 | raise ReleaseNotFound(release_name) 40 | 41 | def get_assets(self, release_name, asset_names): 42 | release = self.release_with_name(release_name) 43 | 44 | result = [] 45 | 46 | for asset_name in asset_names: 47 | found = False 48 | for asset in release['assets']: 49 | if asset['name'] == asset_name: 50 | result.append(asset) 51 | found = True 52 | break 53 | if not found: 54 | raise ReleaseMissingRequiredAsset(release_name, asset_name) 55 | 56 | return result 57 | 58 | 59 | def download_and_install_release(self, release_name, install_dir, asset_names): 60 | """Download a release named `release_name` and install into dir 61 | `release_dir`. 62 | """ 63 | 64 | assets = self.get_assets(release_name, asset_names) 65 | for asset in assets: 66 | if self.auth_token: 67 | req = urllib.request.Request(asset['url']) 68 | req.add_header("Authorization", f"token {self.auth_token}") 69 | req.add_header("Accept", "Accept: application/octet-stream") 70 | utils.download_file(req, install_dir / asset['name']) 71 | else: 72 | utils.download_file(asset['browser_download_url'], install_dir / asset['name']) 73 | 74 | 75 | -------------------------------------------------------------------------------- /keyless-common/src/input_processing/circuit_config.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Aptos Foundation 2 | 3 | use anyhow::anyhow; 4 | use serde::{Deserialize, Serialize}; 5 | use std::collections::BTreeMap; 6 | 7 | /// The configuration for a circuit, including maximum lengths for input signals 8 | #[derive(Serialize, Deserialize, Clone)] 9 | #[serde(default, deny_unknown_fields)] 10 | pub struct CircuitConfig { 11 | max_lengths: BTreeMap, 12 | has_input_skip_aud_checks: bool, 13 | } 14 | 15 | impl Default for CircuitConfig { 16 | fn default() -> Self { 17 | Self::new() 18 | } 19 | } 20 | 21 | impl CircuitConfig { 22 | pub fn new() -> Self { 23 | Self { 24 | max_lengths: BTreeMap::new(), 25 | has_input_skip_aud_checks: false, // Do not skip by default 26 | } 27 | } 28 | 29 | /// Returns a reference to all maximum lengths 30 | pub fn all_max_lengths(&self) -> &BTreeMap { 31 | &self.max_lengths 32 | } 33 | 34 | /// Gets the maximum length for a given signal 35 | pub fn get_max_length(&self, key: &str) -> anyhow::Result { 36 | let max_length = self 37 | .max_lengths 38 | .get(key) 39 | .ok_or_else(|| anyhow!("Can't find key {} in circuit config!", key))?; 40 | Ok(*max_length) 41 | } 42 | 43 | /// Returns whether the circuit config has the input signal to skip audit checks 44 | pub fn has_input_skip_aud_checks(&self) -> bool { 45 | self.has_input_skip_aud_checks 46 | } 47 | 48 | /// Sets the maximum length for a given signal, and returns the updated config 49 | pub fn max_length(mut self, signal: &str, length: usize) -> Self { 50 | self.max_lengths.insert(signal.to_string(), length); 51 | self 52 | } 53 | } 54 | 55 | #[cfg(test)] 56 | mod tests { 57 | use super::*; 58 | 59 | #[test] 60 | fn test_circuit_config_max_length() { 61 | // Create a circuit config with some max lengths 62 | let config = CircuitConfig::new() 63 | .max_length("signal_a", 128) 64 | .max_length("signal_b", 256); 65 | 66 | // Test retrieval of max lengths 67 | assert_eq!(config.get_max_length("signal_a").unwrap(), 128); 68 | assert_eq!(config.get_max_length("signal_b").unwrap(), 256); 69 | assert!(config.get_max_length("signal_c").is_err()); 70 | } 71 | 72 | #[test] 73 | fn test_circuit_config_has_input_skip_aud_checks() { 74 | // Create a default circuit config 75 | let config = CircuitConfig::new(); 76 | 77 | // Test that the default value is false 78 | assert!(!config.has_input_skip_aud_checks()); 79 | 80 | // Create a circuit config with skip audit checks enabled 81 | let config_with_skip = CircuitConfig { 82 | max_lengths: BTreeMap::new(), 83 | has_input_skip_aud_checks: true, 84 | }; 85 | 86 | // Test that the value is true 87 | assert!(config_with_skip.has_input_skip_aud_checks()); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /circuit/templates/helpers/base64url/Base64UrlDecode.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "./Base64UrlLookup.circom"; 4 | 5 | include "circomlib/circuits/bitify.circom"; 6 | 7 | // Base64url-decodes an array of bytes into an array. 8 | // 9 | // @param N the maximum length of the *decoded* output, in bytes 10 | // 11 | // @input in the base64url-encoded input, as an array of zero-padded bytes 12 | // 13 | // @output out the decoded output, as an array of bytes 14 | // 15 | // @notes 16 | // Assumes `in` contains only base64url characters followed by 0-padding. 17 | template Base64UrlDecode(N) { 18 | // If this was padded base64url, then the encoded input's maximum length is 4 * \ceil{N / 3}. 19 | // (We previously implemented it as: 4 * \floor{(N + 2) / 3}.) 20 | // Examples: 21 | // N = 0 => M = 0 22 | // N = 1 => M = 4 23 | // N = 2 => M = 4 24 | // N = 3 => M = 4 25 | // N = 4 => M = 8 26 | // 27 | // If this was *un*padded, as is the case for JWTs, the max encoded length is \ceil{4N / 3}. 28 | // Examples: 29 | // N = 0 => M = 0 30 | // N = 1 => M = 2 31 | // N = 2 => M = 3 32 | // N = 3 => M = 4 33 | // N = 4 => M = 6 34 | // (We implement this as: \floor{(4*N + 2) / 3}.) 35 | var M = (4*N + 2) \ 3; 36 | signal input in[M]; 37 | signal output out[N]; 38 | 39 | component bits_in[M \ 4][4]; 40 | component bits_out[M \ 4][3]; 41 | component translate[M \ 4][4]; 42 | 43 | var idx = 0; 44 | for (var i = 0; i < M; i += 4) { 45 | for (var j = 0; j < 3; j++) { 46 | bits_out[i \ 4][j] = Bits2Num(8); 47 | } 48 | 49 | //log("range_AZ: ", range_AZ); 50 | for (var j = 0; j < 4; j++) { 51 | bits_in[i \ 4][j] = Num2Bits(6); 52 | 53 | //log("translate[i \\ 4][j].in: ", in[i + j]); 54 | 55 | translate[i \ 4][j] = Base64UrlLookup(); 56 | translate[i \ 4][j].in <== in[i + j]; 57 | translate[i \ 4][j].out ==> bits_in[i \ 4][j].in; 58 | } 59 | 60 | // Do the re-packing from four 6-bit words to three 8-bit words. 61 | for (var j = 0; j < 6; j++) { 62 | bits_out[i \ 4][0].in[j+2] <== bits_in[i \ 4][0].out[j]; 63 | } 64 | bits_out[i \ 4][0].in[0] <== bits_in[i \ 4][1].out[4]; 65 | bits_out[i \ 4][0].in[1] <== bits_in[i \ 4][1].out[5]; 66 | 67 | for (var j = 0; j < 4; j++) { 68 | bits_out[i \ 4][1].in[j + 4] <== bits_in[i \ 4][1].out[j]; 69 | } 70 | for (var j = 0; j < 4; j++) { 71 | bits_out[i \ 4][1].in[j] <== bits_in[i \ 4][2].out[j + 2]; 72 | } 73 | 74 | bits_out[i \ 4][2].in[6] <== bits_in[i \ 4][2].out[0]; 75 | bits_out[i \ 4][2].in[7] <== bits_in[i \ 4][2].out[1]; 76 | for (var j = 0; j < 6; j++) { 77 | bits_out[i \ 4][2].in[j] <== bits_in[i \ 4][3].out[j]; 78 | } 79 | 80 | for (var j = 0; j < 3; j++) { 81 | if (idx + j < N) { 82 | out[idx + j] <== bits_out[i \ 4][j].out; 83 | } 84 | } 85 | idx += 3; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /scripts/task.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | SCRIPT_DIR=$(dirname "$0") 6 | 7 | # Installs rustup if not already installed 8 | function install_rustup { 9 | VERSION="$(rustup --version || true)" 10 | if [ -n "$VERSION" ]; then 11 | if [[ "${BATCH_MODE}" == "false" ]]; then 12 | echo "Rustup is already installed, version: $VERSION" 13 | fi 14 | else 15 | curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable 16 | if [[ -n "${CARGO_HOME}" ]]; then 17 | PATH="${CARGO_HOME}/bin:${PATH}" 18 | else 19 | PATH="${HOME}/.cargo/bin:${PATH}" 20 | fi 21 | fi 22 | } 23 | 24 | # Installs cargo-machete if not already installed 25 | function install_cargo_machete { 26 | if ! command -v cargo-machete &>/dev/null; then 27 | cargo install cargo-machete --locked --version 0.7.0 28 | fi 29 | } 30 | 31 | # Installs cargo-sort if not already installed 32 | function install_cargo_sort { 33 | if ! command -v cargo-sort &>/dev/null; then 34 | cargo install cargo-sort --locked --version 1.0.7 35 | fi 36 | } 37 | 38 | # Installs necessary dependencies 39 | install_deps() { 40 | # Install python3 and curl if not present 41 | if ! command -v python3 > /dev/null || ! command -v curl > /dev/null; then 42 | OS=$(uname -s) 43 | case $OS in 44 | Linux*) 45 | if command -v apt-get > /dev/null; then 46 | if command -v sudo > /dev/null; then 47 | sudo apt-get update 48 | sudo apt-get install -y python3 python3-pip pipx curl 49 | else 50 | apt-get update 51 | apt-get install -y python3 python3-pip pipx curl 52 | fi 53 | elif command -v pacman > /dev/null; then 54 | if command -v sudo > /dev/null; then 55 | sudo pacman -Syu --noconfirm 56 | sudo pacman -S --needed --noconfirm python python-pip python-pipx curl 57 | pipx install invoke 58 | else 59 | pacman -Syu --noconfirm 60 | pacman -S --needed --noconfirm python python-pip python-pipx curl 61 | fi 62 | else 63 | >&2 echo "No suitable package manager found for Linux." 64 | fi 65 | ;; 66 | Darwin*) 67 | if command -v brew > /dev/null; then 68 | brew install python 69 | else 70 | >&2 echo "Homebrew is not installed. Install Homebrew to use this." 71 | fi 72 | ;; 73 | *) 74 | >&2 echo "Unsupported OS: $OS" 75 | ;; 76 | esac 77 | >&2 echo "Dependencies installation finished." 78 | fi 79 | 80 | # Install rustup 81 | install_rustup 82 | 83 | # Install cargo dependency tools 84 | install_cargo_machete 85 | install_cargo_sort 86 | } 87 | 88 | install_deps 89 | 90 | if ! ls .venv &> /dev/null; then 91 | python3 -m venv .venv 92 | fi 93 | if ! .venv/bin/pip3 show google-cloud-storage typer &> /dev/null; then 94 | .venv/bin/pip3 install google-cloud-storage typer &> /dev/null 95 | fi 96 | 97 | .venv/bin/python3 $SCRIPT_DIR/python/main.py "$@" 98 | 99 | 100 | -------------------------------------------------------------------------------- /circuit/templates/helpers/base64url/Base64UrlLookup.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "circomlib/circuits/comparators.circom"; 4 | 5 | // Given an 8-bit base64 character, returns its 6-bit decoding. 6 | // Handles the '=' base64 padding character, even though it is not needed for JWTs. 7 | // 8 | // @input in the 8-bit base64 alphabet character 9 | // 10 | // @output out the 6-bit decoded bits corresponding to `in` 11 | // 12 | // @notes 13 | // From [here](http://0x80.pl/notesen/2016-01-17-sse-base64-decoding.html#vector-lookup-base), but 14 | // modified to support base64url instead. 15 | template Base64UrlLookup() { 16 | signal input in; 17 | signal output out; 18 | 19 | // ['A', 'Z'] 20 | component le_Z = LessThan(8); 21 | le_Z.in[0] <== in; 22 | le_Z.in[1] <== 90+1; 23 | 24 | component ge_A = GreaterThan(8); 25 | ge_A.in[0] <== in; 26 | ge_A.in[1] <== 65-1; 27 | 28 | signal range_AZ <== ge_A.out * le_Z.out; 29 | signal sum_AZ <== range_AZ * (in - 65); 30 | 31 | // ['a', 'z'] 32 | component le_z = LessThan(8); 33 | le_z.in[0] <== in; 34 | le_z.in[1] <== 122+1; 35 | 36 | component ge_a = GreaterThan(8); 37 | ge_a.in[0] <== in; 38 | ge_a.in[1] <== 97-1; 39 | 40 | signal range_az <== ge_a.out * le_z.out; 41 | signal sum_az <== sum_AZ + range_az * (in - 71); 42 | 43 | // ['0', '9'] 44 | component le_9 = LessThan(8); 45 | le_9.in[0] <== in; 46 | le_9.in[1] <== 57+1; 47 | 48 | component ge_0 = GreaterThan(8); 49 | ge_0.in[0] <== in; 50 | ge_0.in[1] <== 48-1; 51 | 52 | signal range_09 <== ge_0.out * le_9.out; 53 | signal sum_09 <== sum_az + range_09 * (in + 4); 54 | 55 | // '-' 56 | component equal_minus = IsZero(); 57 | equal_minus.in <== in - 45; 58 | // https://www.cs.cmu.edu/~pattis/15-1XX/common/handouts/ascii.html ascii '-' (45) 59 | // https://base64.guru/learn/base64-characters == 62 in base64 60 | signal sum_minus <== sum_09 + equal_minus.out * 62; 61 | 62 | // '_' 63 | component equal_underscore = IsZero(); 64 | equal_underscore.in <== in - 95; 65 | // https://www.cs.cmu.edu/~pattis/15-1XX/common/handouts/ascii.html ascii '_' (95) 66 | // https://base64.guru/learn/base64-characters == 63 in base64 67 | signal sum_underscore <== sum_minus + equal_underscore.out * 63; 68 | 69 | out <== sum_underscore; 70 | //log("sum_underscore (out): ", out); 71 | 72 | // '=' 73 | component equal_eqsign = IsZero(); 74 | equal_eqsign.in <== in - 61; 75 | 76 | // Also decode zero padding as zero padding 77 | component zero_padding = IsZero(); 78 | zero_padding.in <== in; 79 | 80 | //log("zero_padding.out: ", zero_padding.out); 81 | //log("equal_eqsign.out: ", equal_eqsign.out); 82 | //log("equal_underscore.out: ", equal_underscore.out); 83 | //log("equal_minus.out: ", equal_minus.out); 84 | //log("range_09: ", range_09); 85 | //log("range_az: ", range_az); 86 | //log("range_AZ: ", range_AZ); 87 | 88 | signal result <== range_AZ + range_az + range_09 + equal_minus.out + equal_underscore.out + equal_eqsign.out + zero_padding.out; 89 | 1 === result; 90 | } -------------------------------------------------------------------------------- /circuit/templates/helpers/strings/AssertIsConcatenation.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | include "../hashtofield/HashBytesToFieldWithLen.circom"; 4 | 5 | include "../arrays/RightArraySelector.circom"; 6 | include "../arrays/SelectArrayValue.circom"; 7 | 8 | include "../../stdlib/circuits/Sum.circom"; 9 | 10 | include "circomlib/circuits/comparators.circom"; 11 | include "circomlib/circuits/poseidon.circom"; 12 | 13 | // Given `full_string`, `left`, and `right`, checks that full_string = left || right 14 | // `random_challenge` is expected to be computed by the Fiat-Shamir transform 15 | // Assumes `right_len` has been validated to be correct outside of this subcircuit, i.e. that 16 | // `right` is 0-padded after `right_len` values 17 | // Enforces: 18 | // - that `left` is 0-padded after `left_len` values 19 | // - full_string = left || right where || is concatenation 20 | template AssertIsConcatenation(MAX_FULL_STR_LEN, MAX_LEFT_STR_LEN, MAX_RIGHT_STR_LEN) { 21 | signal input full_string[MAX_FULL_STR_LEN]; 22 | signal input left[MAX_LEFT_STR_LEN]; 23 | signal input right[MAX_RIGHT_STR_LEN]; 24 | signal input left_len; 25 | signal input right_len; 26 | 27 | signal left_hash <== HashBytesToFieldWithLen(MAX_LEFT_STR_LEN)(left, left_len); 28 | signal right_hash <== HashBytesToFieldWithLen(MAX_RIGHT_STR_LEN)(right, right_len); 29 | signal full_hash <== HashBytesToFieldWithLen(MAX_FULL_STR_LEN)(full_string, left_len+right_len); 30 | signal random_challenge <== Poseidon(4)([left_hash, right_hash, full_hash, left_len]); 31 | 32 | // Enforce that all values to the right of `left_len` in `left` are 0-padding. Otherwise an attacker could place the leftmost part of `right` at the end of `left` and still have the polynomial check pass 33 | signal left_selector[MAX_LEFT_STR_LEN] <== RightArraySelector(MAX_LEFT_STR_LEN)(left_len-1); 34 | for (var i = 0; i < MAX_LEFT_STR_LEN; i++) { 35 | left_selector[i] * left[i] === 0; 36 | } 37 | 38 | signal challenge_powers[MAX_FULL_STR_LEN]; 39 | challenge_powers[0] <== 1; 40 | challenge_powers[1] <== random_challenge; 41 | for (var i = 2; i < MAX_FULL_STR_LEN; i++) { 42 | challenge_powers[i] <== challenge_powers[i-1] * random_challenge; 43 | } 44 | 45 | signal left_poly[MAX_LEFT_STR_LEN]; 46 | for (var i = 0; i < MAX_LEFT_STR_LEN; i++) { 47 | left_poly[i] <== left[i] * challenge_powers[i]; 48 | } 49 | 50 | signal right_poly[MAX_RIGHT_STR_LEN]; 51 | for (var i = 0; i < MAX_RIGHT_STR_LEN; i++) { 52 | right_poly[i] <== right[i] * challenge_powers[i]; 53 | } 54 | 55 | signal full_poly[MAX_FULL_STR_LEN]; 56 | for (var i = 0; i < MAX_FULL_STR_LEN; i++) { 57 | full_poly[i] <== full_string[i] * challenge_powers[i]; 58 | } 59 | 60 | signal left_poly_eval <== Sum(MAX_LEFT_STR_LEN)(left_poly); 61 | signal right_poly_eval <== Sum(MAX_RIGHT_STR_LEN)(right_poly); 62 | signal full_poly_eval <== Sum(MAX_FULL_STR_LEN)(full_poly); 63 | 64 | var distinguishing_value = SelectArrayValue(MAX_FULL_STR_LEN)(challenge_powers, left_len); 65 | 66 | full_poly_eval === left_poly_eval + distinguishing_value * right_poly_eval; 67 | } 68 | -------------------------------------------------------------------------------- /circuit/templates/helpers/rsa/FpMul.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.2.2; 2 | 3 | // File copied from https://github.com/doubleblind-xyz/circom-rsa/blob/master/circuits/fp.circom 4 | 5 | include "circomlib/circuits/bitify.circom"; 6 | 7 | include "../bigint/CheckCarryToZero.circom"; 8 | include "../bigint/functions/all.circom"; 9 | 10 | // These functions operate over values in Z/Zp for some integer p (typically, 11 | // but not necessarily prime). Values are stored as standard bignums with K 12 | // chunks of N bits, but intermediate values often have "overflow" bits inside 13 | // various chunks. 14 | // 15 | // These Fp functions will always correctly generate witnesses mod p, but they 16 | // do not *check* that values are normalized to < p; they only check that 17 | // values are correct mod p. This is to save the comparison circuit. 18 | // They *will* always check for intended results mod p (soundness), but it may 19 | // not have a unique intermediate signal. 20 | // 21 | // Conversely, some templates may not be satisfiable if the input witnesses are 22 | // not < p. This does not break completeness, as honest provers will always 23 | // generate witnesses which are canonical (between 0 and p). 24 | 25 | // a * b = r mod p 26 | // a * b - p * q - r for some q 27 | template FpMul(N, K) { 28 | // TODO: Fix hardcoded scalar field size 29 | assert(N + N + log_ceil(K) + 2 <= 252); 30 | signal input a[K]; 31 | signal input b[K]; 32 | signal input p[K]; 33 | 34 | signal output out[K]; 35 | 36 | signal v_ab[2*K-1]; 37 | for (var x = 0; x < 2*K-1; x++) { 38 | var v_a = poly_eval(K, a, x); 39 | var v_b = poly_eval(K, b, x); 40 | v_ab[x] <== v_a * v_b; 41 | } 42 | 43 | var ab[200] = poly_interp(2*K-1, v_ab); 44 | // ab_proper has length 2*K 45 | var ab_proper[200] = getProperRepresentation(N + N + log_ceil(K), N, 2*K-1, ab); 46 | 47 | var long_div_out[2][100] = long_div(N, K, K, ab_proper, p); 48 | 49 | // Since we're only computing a*b, we know that q < p will suffice, so we 50 | // know it fits into K chunks and can do size N range checks. 51 | signal q[K]; 52 | component q_range_check[K]; 53 | signal r[K]; 54 | component r_range_check[K]; 55 | for (var i = 0; i < K; i++) { 56 | q[i] <-- long_div_out[0][i]; 57 | q_range_check[i] = Num2Bits(N); 58 | q_range_check[i].in <== q[i]; 59 | 60 | r[i] <-- long_div_out[1][i]; 61 | r_range_check[i] = Num2Bits(N); 62 | r_range_check[i].in <== r[i]; 63 | } 64 | 65 | signal v_pq_r[2*K-1]; 66 | for (var x = 0; x < 2*K-1; x++) { 67 | var v_p = poly_eval(K, p, x); 68 | var v_q = poly_eval(K, q, x); 69 | var v_r = poly_eval(K, r, x); 70 | v_pq_r[x] <== v_p * v_q + v_r; 71 | } 72 | 73 | signal v_t[2*K-1]; 74 | for (var x = 0; x < 2*K-1; x++) { 75 | v_t[x] <== v_ab[x] - v_pq_r[x]; 76 | } 77 | 78 | var t[200] = poly_interp(2*K-1, v_t); 79 | component tCheck = CheckCarryToZero(N, N + N + log_ceil(K) + 2, 2*K-1); 80 | for (var i = 0; i < 2*K-1; i++) { 81 | tCheck.in[i] <== t[i]; 82 | } 83 | 84 | for (var i = 0; i < K; i++) { 85 | out[i] <== r[i]; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /rust-rapidsnark/rapidsnark/src/pointparallelprocessor.hpp: -------------------------------------------------------------------------------- 1 | #ifndef POINT_PARALLEL_PROCESSOR_H 2 | #define POINT_PARALLEL_PROCESSOR_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "growablearray_mt.hpp" 9 | 10 | #define NOPS_CHUNK (uint64_t)(1LL<<13) 11 | #define MAX_LEVELS 1024 12 | template 13 | class PointParallelProcessor { 14 | 15 | Curve &curve; 16 | enum Function { ADD, ADD_MIXED, ADD_AFFINE }; 17 | struct Op { 18 | Function fn; 19 | void *r; 20 | void *a; 21 | void *b; 22 | Op(Function _fn, void * _r,void *_a, void *_b) : fn(_fn), r(_r), a(_a), b(_b) {}; 23 | Op() {}; 24 | }; 25 | 26 | public: 27 | enum Source { ZERO=0, BASE=1, HEAP=2}; 28 | 29 | // #pragma pack(push, 1) 30 | struct Point { 31 | Source source; 32 | uint16_t level; 33 | void *p; 34 | }; 35 | // #pragma pack(pop) 36 | 37 | private: 38 | typename Curve::PointAffine *bases; 39 | GrowableArrayMT *heap; 40 | GrowableArrayMT **ops; 41 | uint32_t nLevels; 42 | 43 | bool terminated; 44 | uint32_t nThreads; 45 | uint32_t currentLevel; 46 | typename GrowableArrayMT::Iterator itExecuting; 47 | uint64_t pendingThreads; 48 | 49 | std::vector threads; 50 | std::mutex cv_mutex; 51 | std::condition_variable cv; 52 | 53 | void addOp(uint32_t idThread, uint32_t level, Function fn, Point r, Point a, Point b); 54 | Point allocHeapPoint(uint32_t idThread, uint32_t level); 55 | void *getPointPointer(Point p); 56 | 57 | void childThread(uint32_t th); 58 | void innerProcess(uint32_t level, typename GrowableArrayMT::Iterator start, typename GrowableArrayMT::Iterator end); 59 | 60 | 61 | public: 62 | 63 | PointParallelProcessor(Curve &_curve, uint32_t _nThreads, typename Curve::PointAffine *_bases) : curve(_curve) { 64 | bases = _bases; 65 | nThreads = _nThreads; 66 | terminated = false; 67 | nLevels = 0; 68 | ops = new GrowableArrayMT *[MAX_LEVELS]; 69 | for (uint32_t i=0; i(nThreads); 71 | } 72 | heap = new GrowableArrayMT(nThreads); 73 | } 74 | 75 | ~PointParallelProcessor() { 76 | for (uint32_t i=0; i