Merge pull request #33 from zk-passport/fix-poseidon-bug

Fix poseidon bug
This commit is contained in:
turboblitz
2023-12-30 00:09:47 +01:00
committed by GitHub
13 changed files with 177 additions and 78 deletions

View File

@@ -1 +0,0 @@
{"content_hash_max_items": 1048576}

View File

@@ -306,8 +306,6 @@ function App(): JSX.Element {
setTotalTime(end - start);
setProofResult(JSON.stringify(deserializedProof));
// les outputs publics vont être postés on-chain comment ?
});
};

View File

@@ -286,8 +286,10 @@ dependencies = [
"ark-groth16",
"ark-std",
"color-eyre",
"hex",
"jni",
"log",
"num-bigint",
"serde",
"serde_derive",
"serde_json",

View File

@@ -24,3 +24,7 @@ android_logger = "0.8"
serde = "1.0"
serde_json = "1.0"
serde_derive = "1.0"
hex = "0.4"
num-bigint = { version = "=0.4.3", default-features = false, features = [
"rand",
] }

View File

@@ -1,4 +1,4 @@
use ark_circom::{CircomBuilder, CircomConfig};
use ark_circom::{ethereum, CircomBuilder, CircomConfig};
use ark_std::rand::thread_rng;
use color_eyre::Result;
use std::os::raw::c_int;
@@ -9,6 +9,10 @@ use ark_groth16::{Groth16, Proof};
// use ark_ff::QuadExtField;
use ark_ec::AffineRepr;
use num_bigint::{BigInt, Sign};
extern crate hex;
use hex::decode;
use std::time::Instant;
use std::convert::TryInto;
@@ -36,7 +40,7 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_callRustCode(
_: JClass,
) -> jstring {
android_logger::init_once(Config::default().with_min_level(Level::Trace));
log::warn!("log before imports");
log::error!("log before imports");
let my_int: c_int = -1;
let my_str: String = "no_proof".to_string();
@@ -58,7 +62,7 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_proveRSAInRust(
_: JClass,
) -> c_int {
fn run() -> Result<u128, Box<dyn std::error::Error>> {
println!("log before imports");
log::error!("log before imports");
Ok(10)
}
match run() {
@@ -83,7 +87,7 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
address: JString,
) -> jstring {
log::warn!("formatting inputsaaaa...");
log::error!("formatting inputsaaaa...");
fn run_proof(
mrz: JObject,
reveal_bitmap: JObject,
@@ -96,8 +100,8 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
) -> Result<jstring, Box<dyn std::error::Error>> {
android_logger::init_once(Config::default().with_min_level(Level::Trace));
log::warn!("formatting inputs...");
log::warn!("mrz_veccccccc");
log::error!("formatting inputs...");
log::error!("mrz_veccccccc");
let mrz_vec: Vec<String> = java_arraylist_to_rust_vec(&env, mrz)?;
let reveal_bitmap_vec: Vec<String> = java_arraylist_to_rust_vec(&env, reveal_bitmap)?;
@@ -107,18 +111,18 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
let pubkey_vec: Vec<String> = java_arraylist_to_rust_vec(&env, pubkey)?;
let address_str: String = env.get_string(address)?.into();
log::warn!("mrz_vec {:?}", mrz_vec);
log::warn!("reveal_bitmap_vec {:?}", reveal_bitmap_vec);
log::warn!("data_hashes_vec {:?}", data_hashes_vec);
log::warn!("e_content_bytes_vec {:?}", e_content_bytes_vec);
log::warn!("signature_vec {:?}", signature_vec);
log::warn!("pubkey_vec {:?}", pubkey_vec);
log::warn!("address_str {:?}", address_str);
log::error!("mrz_vec {:?}", mrz_vec);
log::error!("reveal_bitmap_vec {:?}", reveal_bitmap_vec);
log::error!("data_hashes_vec {:?}", data_hashes_vec);
log::error!("e_content_bytes_vec {:?}", e_content_bytes_vec);
log::error!("signature_vec {:?}", signature_vec);
log::error!("pubkey_vec {:?}", pubkey_vec);
log::error!("address_str {:?}", address_str);
log::warn!("loading circuit...");
const MAIN_WASM: &'static [u8] = include_bytes!("../passport/passport.wasm");
const MAIN_R1CS: &'static [u8] = include_bytes!("../passport/passport.r1cs");
log::warn!("circuit loaded");
log::error!("loading circuit...");
const MAIN_WASM: &'static [u8] = include_bytes!("../passport/proof_of_passport.wasm");
const MAIN_R1CS: &'static [u8] = include_bytes!("../passport/proof_of_passport.r1cs");
log::error!("circuit loaded");
let cfg = CircomConfig::<Bn254>::from_bytes(MAIN_WASM, MAIN_R1CS)?;
@@ -143,6 +147,9 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
.filter_map(|s| s.parse::<u128>().ok())
.for_each(|n| builder.push_input("pubkey", n));
let address_bigint = BigInt::from_bytes_be(Sign::Plus, &decode(&address_str[2..])?);
builder.push_input("address", address_bigint);
// create an empty instance for setting it up
let circom = builder.setup();
@@ -150,20 +157,25 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
let params = GrothBn::generate_random_parameters_with_reduction(circom, &mut rng)?;
let circom = builder.build()?;
println!("circuit built");
log::error!("circuit built");
let inputs = circom.get_public_inputs().unwrap();
log::error!("inputs: {:?}", inputs);
let converted_inputs: ethereum::Inputs = inputs.as_slice().into();
let inputs_str: Vec<String> = converted_inputs.0.iter().map(|value| format!("{}", value)).collect();
let serialized_inputs = serde_json::to_string(&inputs_str).unwrap();
log::error!("Serialized inputs: {:?}", serialized_inputs);
let start1 = Instant::now();
let proof = GrothBn::prove(&params, circom, &mut rng)?;
let proof_str = proof_to_proof_str(&proof);
let serialized_proof = serde_json::to_string(&proof_str).unwrap();
let duration1 = start1.elapsed();
println!("proof generated. Took: {:?}", duration1);
log::error!("proof generated. Took: {:?}", duration1);
let start2 = Instant::now();
@@ -171,13 +183,14 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
let verified = GrothBn::verify_with_processed_vk(&pvk, &inputs, &proof)?;
let duration2 = start2.elapsed();
println!("proof verified. Took: {:?}", duration2);
log::error!("proof verified. Took: {:?}", duration2);
assert!(verified);
let combined = json!({
"duration": duration1.as_millis(),
"serialized_proof": serialized_proof
"serialized_proof": serialized_proof,
"serialized_inputs": serialized_inputs
});
let combined_str = combined.to_string();

View File

@@ -6,12 +6,5 @@ cd ../android
cd ..
mkdir -p android/react-native-passport-reader/android/src/main/jniLibs/arm64/
# if [ -f "ark-circom-passport/target/aarch64-linux-android/release/libark_circom_passport.so" ]; then
echo copied release version
cp ark-circom-passport/target/aarch64-linux-android/release/libark_circom_passport.so android/react-native-passport-reader/android/src/main/jniLibs/arm64/
# elif [ -f "ark-circom-passport/target/aarch64-linux-android/debug/libark_circom_passport.so" ]; then
# echo copied debug version
# cp ark-circom-passport/target/aarch64-linux-android/debug/libark_circom_passport.so android/react-native-passport-reader/android/src/main/jniLibs/arm64/
# fi
echo copied release version

View File

@@ -20,6 +20,12 @@ yarn
./scripts/build_circuit.sh
```
#### Build only to use the app, not for running tests (dev only, not secure)
```bash
./scripts/build_circuit.sh app-only
```
#### Run tests
```bash

View File

@@ -33,24 +33,16 @@ template ProofOfPassport(n, k) {
// we take nullifier = signature[0, 1] which it 64 + 64 bits long, so chance of collision is 2^128
signal output nullifier <== signature[0] * 2**64 + signature[1];
// Calculate the Poseidon hash of public public key and outputs it
// This can be used to verify the public key is correct in contract without requiring the actual key
// We are converting pub_key (modulus) in to 9 chunks of 242 bits, assuming original n, k are 121 and 17.
// This is because Posiedon circuit only support array of 16 elements.
// Otherwise we would have to output the ceil(256/31) = 9 field elements of the public key
var k2_chunked_size = k >> 1;
if(k % 2 == 1) {
k2_chunked_size += 1;
}
signal pubkey_hash_input[k2_chunked_size];
for(var i = 0; i < k2_chunked_size; i++) {
if(i==k2_chunked_size-1 && k2_chunked_size % 2 == 1) {
pubkey_hash_input[i] <== pubkey[2*i];
// we don't do Poseidon hash cuz it makes arkworks crash for obscure reasons
// we output the pubkey as 11 field elements. 9 is doable also cuz ceil(254/31) = 9
signal output pubkey_packed[11];
for (var i = 0; i < 11; i++) {
if (i < 10) {
pubkey_packed[i] <== pubkey[3*i] * 64 * 64 + pubkey[3*i + 1] * 64 + pubkey[3*i + 2];
} else {
pubkey_hash_input[i] <== pubkey[2*i] + (1<<n) * pubkey[2*i+1];
pubkey_packed[i] <== pubkey[3*i] * 64 * 64;
}
}
signal output pubkey_hash <== Poseidon(k2_chunked_size)(pubkey_hash_input);
}
component main { public [ address ] } = ProofOfPassport(64, 32);

View File

@@ -1,3 +1,12 @@
#!/bin/bash
# Check if the first argument is "app-only"
if [ "$1" == "app-only" ]; then
echo "Building only for the app"
APP_ONLY=1
else
APP_ONLY=0
fi
mkdir -p build
cd build
@@ -13,6 +22,19 @@ cd ..
echo "compiling circuit"
circom circuits/proof_of_passport.circom --r1cs --sym --wasm --output build
mkdir -p ../app/ark-circom-passport/passport/
cp build/proof_of_passport.r1cs ../app/ark-circom-passport/passport/
cp build/proof_of_passport_js/proof_of_passport.wasm ../app/ark-circom-passport/passport/
echo "copied proof_of_passport.r1cs and proof_of_passport.wasm to ark-circom-passport"
echo "file sizes:"
echo "Size of proof_of_passport.r1cs: $(wc -c <../app/ark-circom-passport/passport/proof_of_passport.r1cs) bytes"
echo "Size of proof_of_passport.wasm: $(wc -c <../app/ark-circom-passport/passport/proof_of_passport.wasm) bytes"
# If APP_ONLY is 1, exit the script here
if [ $APP_ONLY -eq 1 ]; then
exit 0
fi
echo "building zkey"
yarn snarkjs groth16 setup build/proof_of_passport.r1cs build/powersOfTau28_hez_final_20.ptau build/proof_of_passport.zkey
@@ -20,4 +42,6 @@ echo "building vkey"
echo "test random" | yarn snarkjs zkey contribute build/proof_of_passport.zkey build/proof_of_passport_final.zkey
yarn snarkjs zkey export verificationkey build/proof_of_passport_final.zkey build/verification_key.json
yarn snarkjs zkey export solidityverifier build/proof_of_passport_final.zkey build/Verifier.sol
yarn snarkjs zkey export solidityverifier build/proof_of_passport_final.zkey build/Verifier.sol
cp build/Verifier.sol ../contracts/contracts/Verifier.sol
echo "copied Verifier.sol to contracts"

View File

@@ -3,14 +3,14 @@ import chai, { assert, expect } from 'chai'
import chaiAsPromised from 'chai-as-promised'
import { hash, toUnsignedByte, arraysAreEqual, bytesToBigDecimal, formatAndConcatenateDataHashes, formatMrz, splitToWords } from '../../common/src/utils/utils'
import { groth16 } from 'snarkjs'
import { DataHash, PassportData } from '../../common/src/utils/types'
import { DataHash } from '../../common/src/utils/types'
import { getPassportData } from '../../common/src/utils/passportData'
import { attributeToPosition } from '../../common/src/constants/constants'
const fs = require('fs');
chai.use(chaiAsPromised)
console.log("The snarkjs following error logs are normal and expected if the tests pass.")
console.log("The following snarkjs error logs are normal and expected if the tests pass.")
describe('Circuit tests', function () {
this.timeout(0)

View File

@@ -59,7 +59,7 @@ contract ProofOfPassport is ERC721Enumerable, Ownable {
uint256[2] memory a,
uint256[2][2] memory b,
uint256[2] memory c,
uint256[6] memory inputs
uint256[16] memory inputs
) public {
// check that the nullifier has not been used before
require(!nullifiers[inputs[3]], "Signature already nullified");
@@ -79,7 +79,7 @@ contract ProofOfPassport is ERC721Enumerable, Ownable {
require(verifier.verifyProof(a, b, c, inputs), "Invalid Proof");
// Effects: Mint token
address addr = address(uint160(inputs[5]));
address addr = address(uint160(inputs[inputs.length - 1])); // generally the last one
uint256 newTokenId = totalSupply();
_mint(addr, newTokenId);
nullifiers[inputs[3]] = true;
@@ -120,7 +120,7 @@ contract ProofOfPassport is ERC721Enumerable, Ownable {
return bytesArray;
}
function sliceFirstThree(uint256[6] memory input) public pure returns (uint256[3] memory) {
function sliceFirstThree(uint256[16] memory input) public pure returns (uint256[3] memory) {
uint256[3] memory sliced;
for (uint256 i = 0; i < 3; i++) {

View File

@@ -37,32 +37,62 @@ contract Groth16Verifier {
uint256 constant gammax2 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
uint256 constant gammay1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
uint256 constant gammay2 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;
uint256 constant deltax1 = 11409087990096298250064903545127759218397922522341756350270902130281319957956;
uint256 constant deltax2 = 1513329647410778948720563322730598607273692918811499888443933638428694484763;
uint256 constant deltay1 = 7078242965940422908272110886514923952615442802227129276594761904085001246704;
uint256 constant deltay2 = 10250312646645778718243542345726538268226010021293194740995761760204942710018;
uint256 constant deltax1 = 3037890460429929853828629296742086109008517454031147703442813108881426331381;
uint256 constant deltax2 = 11415705872230497398160420978210813941423805400115940468997080730756770095941;
uint256 constant deltay1 = 4781886835288895006988886960857872859778352874458931312709265942186357137609;
uint256 constant deltay2 = 6660151851452724449234553790200573309196429136517999261040530971073361464771;
uint256 constant IC0x = 5273738681235489626795253249156042954686324696253697750333394380857132417839;
uint256 constant IC0y = 9857192952041840089161633994514015537135554971079134835409588292482738477181;
uint256 constant IC0x = 4897812530436581420070048815704719785256466787655503857610889333796081821201;
uint256 constant IC0y = 21324217308758963004039464033551222626995062428505595457846546072979649950535;
uint256 constant IC1x = 13099273046850541163676426713665613850498239380428162716631655985866490422114;
uint256 constant IC1y = 805402081419020794565351139094125407243790898682279460683021914187983510021;
uint256 constant IC1x = 3402067829842345916430895428185245090645621522198317090797379965574771542636;
uint256 constant IC1y = 15689341079133962080137763365487039194375030636040098473539254350433970371666;
uint256 constant IC2x = 4265450637747300781024965299586779540792520368072350700653387749302516246471;
uint256 constant IC2y = 7463843387891834357375737290177319173209344475125750726872035788722899299932;
uint256 constant IC2x = 17395362500217368985868893090734905039167826866091632900857231867723495577602;
uint256 constant IC2y = 7930260148586132748363663695528567251481992615924576457603406418853534765181;
uint256 constant IC3x = 20088836232532557466758901319787909748432281529666059108643164527823125804789;
uint256 constant IC3y = 16147539042430614121465862556890947026732365730825643639125269303010476393164;
uint256 constant IC3x = 585494952178863414068208567603839798093905284375122061973360892129986520320;
uint256 constant IC3y = 9052106461455604192832593945466435172808609013430707922648579604917214395471;
uint256 constant IC4x = 24929856770016377874928422014228270742044953121105820213504610769400107305;
uint256 constant IC4y = 19728513400524546085926773844123831498204802391551889897087063418483261808497;
uint256 constant IC4x = 7009544555987721721525965879762930953012066717281509356647068054408609863246;
uint256 constant IC4y = 9820829334259318510794834811753840115411696228971683399345859623869811549881;
uint256 constant IC5x = 16348551320150140412227080661010174367526100186688295471629946307478569964727;
uint256 constant IC5y = 20290740830545611972538939670712192944691799477169965616578940279198383183813;
uint256 constant IC5x = 14238418207024939545815829157744718461870627038741425938816007286311574134474;
uint256 constant IC5y = 281828200475697177916593309667507636329345262293367509851063337891478088781;
uint256 constant IC6x = 6054573107363011273023497115562598583444742570417299435209185300867053768320;
uint256 constant IC6y = 10299856535915777925194272650832527282284880643409307207104854021775505827551;
uint256 constant IC6x = 2588875801176981985506740691573068253787601389179711941209167172577223524972;
uint256 constant IC6y = 15518320710469760878810555468816349074382837865578096899468878146062509663814;
uint256 constant IC7x = 17316533695265131380437649603796400699657451230738779001098608159520582988369;
uint256 constant IC7y = 8192809588256960378913803069056080395056022087843425771582496472786713726350;
uint256 constant IC8x = 7407001681033909392094003743482787694603426004447721439357204522366208646546;
uint256 constant IC8y = 14678011064151490372018354732758508739891026527348606433971585164806457243690;
uint256 constant IC9x = 14324550585252189304511012915310919737099760521255841560510202883547241215550;
uint256 constant IC9y = 17843516173433864891394764190574725294294272661192260445890002683170043518436;
uint256 constant IC10x = 7539891259658208616633326740578026995822087197681333710337145994575633967330;
uint256 constant IC10y = 11252528180616460725708054390959401682257312545535188972038868508936671228701;
uint256 constant IC11x = 13300379642556942794398405666278194834327040509717856013516915921151482858342;
uint256 constant IC11y = 3765685546823952453740511654773017065896884149593650080085427606727523849353;
uint256 constant IC12x = 9268919849281685133024784239234560640021437913056581963083327429501365255031;
uint256 constant IC12y = 11222912618976361984001818562665888490194957370177999142904578411305511279126;
uint256 constant IC13x = 2597478889552428352737130179687206531821645186216868965539289093449836402726;
uint256 constant IC13y = 757968852987628828382971340903318389342984851650872494795704692702408158904;
uint256 constant IC14x = 16147563600769233454259564579865458680953713847620754688678964339139397943562;
uint256 constant IC14y = 3229058257062194976564868360849873757615447419031013710509312378420932332089;
uint256 constant IC15x = 11276405747528923910383092138862864843497716277810279291090775583122182049041;
uint256 constant IC15y = 17478497004985764197329627914040721906294759410027200889688899456335265284727;
uint256 constant IC16x = 7537276704716430448981792598508402432998887447285614055846784939499149706536;
uint256 constant IC16y = 13397681836333574838145763582606233729786782316119672353292568940401561429759;
// Memory data
@@ -71,7 +101,7 @@ contract Groth16Verifier {
uint16 constant pLastMem = 896;
function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[6] calldata _pubSignals) public view returns (bool) {
function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[16] calldata _pubSignals) public view returns (bool) {
assembly {
function checkField(v) {
if iszero(lt(v, q)) {
@@ -127,6 +157,26 @@ contract Groth16Verifier {
g1_mulAccC(_pVk, IC6x, IC6y, calldataload(add(pubSignals, 160)))
g1_mulAccC(_pVk, IC7x, IC7y, calldataload(add(pubSignals, 192)))
g1_mulAccC(_pVk, IC8x, IC8y, calldataload(add(pubSignals, 224)))
g1_mulAccC(_pVk, IC9x, IC9y, calldataload(add(pubSignals, 256)))
g1_mulAccC(_pVk, IC10x, IC10y, calldataload(add(pubSignals, 288)))
g1_mulAccC(_pVk, IC11x, IC11y, calldataload(add(pubSignals, 320)))
g1_mulAccC(_pVk, IC12x, IC12y, calldataload(add(pubSignals, 352)))
g1_mulAccC(_pVk, IC13x, IC13y, calldataload(add(pubSignals, 384)))
g1_mulAccC(_pVk, IC14x, IC14y, calldataload(add(pubSignals, 416)))
g1_mulAccC(_pVk, IC15x, IC15y, calldataload(add(pubSignals, 448)))
g1_mulAccC(_pVk, IC16x, IC16y, calldataload(add(pubSignals, 480)))
// -A
mstore(_pPairing, calldataload(pA))
@@ -194,6 +244,26 @@ contract Groth16Verifier {
checkField(calldataload(add(_pubSignals, 192)))
checkField(calldataload(add(_pubSignals, 224)))
checkField(calldataload(add(_pubSignals, 256)))
checkField(calldataload(add(_pubSignals, 288)))
checkField(calldataload(add(_pubSignals, 320)))
checkField(calldataload(add(_pubSignals, 352)))
checkField(calldataload(add(_pubSignals, 384)))
checkField(calldataload(add(_pubSignals, 416)))
checkField(calldataload(add(_pubSignals, 448)))
checkField(calldataload(add(_pubSignals, 480)))
checkField(calldataload(add(_pubSignals, 512)))
// Validate all evaluations
let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem)

View File

@@ -9,7 +9,6 @@ import { groth16 } from 'snarkjs'
import { countryCodes } from "../../common/src/constants/constants";
import { time } from "@nomicfoundation/hardhat-toolbox/network-helpers";
import axios from 'axios';
const fs = require('fs');
describe("Proof of Passport", function () {
@@ -93,7 +92,6 @@ describe("Proof of Passport", function () {
console.log('proof done');
const revealChars = publicSignals.slice(0, 88).map((byte: string) => String.fromCharCode(parseInt(byte, 10))).join('');
// console.log('reveal chars', revealChars);
const vKey = JSON.parse(fs.readFileSync("../circuits/build/verification_key.json"));
const verified = await groth16.verify(
@@ -116,7 +114,7 @@ describe("Proof of Passport", function () {
const { verifier, callData } = await loadFixture(deployFixture);
expect(
await verifier.verifyProof(...callData)
await verifier.verifyProof(callData[0], callData[1], callData[2], callData[3])
).to.be.true;
});
@@ -222,7 +220,7 @@ describe("Proof of Passport", function () {
})
});
describe.only("Minting using lambda function", function () {
describe("Minting using lambda function", function () {
it.skip("Should allow minting using lambda function", async function () {
const { callData } = await loadFixture(
deployFixture