mirror of
https://github.com/selfxyz/self.git
synced 2026-04-27 03:01:15 -04:00
Merge pull request #59 from zk-passport/android-release
Android release
This commit is contained in:
@@ -283,7 +283,7 @@ function App(): JSX.Element {
|
||||
}
|
||||
}
|
||||
|
||||
const handleProve = async () => {
|
||||
const handleProve = async (path: string) => {
|
||||
setStep(Steps.GENERATING_PROOF);
|
||||
if (passportData === null) {
|
||||
console.log('passport data is null');
|
||||
@@ -347,7 +347,7 @@ function App(): JSX.Element {
|
||||
|
||||
const start = Date.now();
|
||||
if (Platform.OS === 'android') {
|
||||
await proveAndroid(inputs);
|
||||
await proveAndroid(inputs, path);
|
||||
} else {
|
||||
await proveIOS(inputs);
|
||||
}
|
||||
@@ -355,8 +355,8 @@ function App(): JSX.Element {
|
||||
console.log('Total proof time from frontend:', end - start);
|
||||
};
|
||||
|
||||
async function proveAndroid(inputs: any) {
|
||||
NativeModules.RNPassportReader.provePassport(inputs, (err: any, res: any) => {
|
||||
async function proveAndroid(inputs: any, path: string) {
|
||||
NativeModules.RNPassportReader.provePassport(inputs, path, (err: any, res: any) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
setError(
|
||||
|
||||
@@ -87,3 +87,17 @@ If you want to mint a proof of passport SBT, instead of building the circuit you
|
||||
|
||||
This will download the zkey currently deployed onchain in the proof of passport contract and place it in `circuits/build``
|
||||
Then, build the android or iOS native module and run the app.
|
||||
|
||||
|
||||
#### Releases
|
||||
|
||||
##### Play Store
|
||||
As explained [here](https://reactnative.dev/docs/signed-apk-android), first setup `android/app/my-upload-key.keystore` and the private vars in `~/.gradle/gradle.properties`, then run:
|
||||
```
|
||||
npx react-native build-android --mode=release
|
||||
```
|
||||
This builds `android/app/build/outputs/bundle/release/app-release.aab`.
|
||||
Then to test the release on an android phone, delete the previous version of the app and run:
|
||||
```
|
||||
yarn android --mode release
|
||||
```
|
||||
@@ -88,15 +88,21 @@ android {
|
||||
keyAlias 'androiddebugkey'
|
||||
keyPassword 'android'
|
||||
}
|
||||
release {
|
||||
if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
|
||||
storeFile file(MYAPP_UPLOAD_STORE_FILE)
|
||||
storePassword MYAPP_UPLOAD_STORE_PASSWORD
|
||||
keyAlias MYAPP_UPLOAD_KEY_ALIAS
|
||||
keyPassword MYAPP_UPLOAD_KEY_PASSWORD
|
||||
}
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
debug {
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
release {
|
||||
// Caution! In production, you need to generate your own keystore file.
|
||||
// see https://reactnative.dev/docs/signed-apk-android.
|
||||
signingConfig signingConfigs.debug
|
||||
signingConfig signingConfigs.release
|
||||
minifyEnabled enableProguardInReleaseBuilds
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
node_modules/
|
||||
android/src/main/jniLibs/arm64/libark_circom_passport.so
|
||||
android/src/main/jniLibs/arm64-v8a/libark_circom_passport.so
|
||||
@@ -1,4 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.tradle.nfc">
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-feature android:name="android.hardware.nfc" android:required="false" />
|
||||
</manifest>
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.text.TextWatcher
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import android.widget.EditText
|
||||
import android.content.Context
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import io.tradle.nfc.ImageUtil.decodeImage
|
||||
@@ -68,7 +69,9 @@ import java.io.ByteArrayInputStream
|
||||
import java.io.DataInputStream
|
||||
import java.io.InputStream
|
||||
import java.io.IOException
|
||||
import java.io.FileOutputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.security.KeyStore
|
||||
import java.security.MessageDigest
|
||||
import java.security.Signature
|
||||
@@ -608,12 +611,13 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
|
||||
eContentBytes: List<String>,
|
||||
signature: List<String>,
|
||||
pubkey: List<String>,
|
||||
address: String
|
||||
address: String,
|
||||
zkeypath: String
|
||||
): String
|
||||
|
||||
@ReactMethod
|
||||
fun provePassport(inputs: ReadableMap, callback: Callback) {
|
||||
Log.d(TAG, "inputsaaa: " + inputs.toString())
|
||||
fun provePassport(inputs: ReadableMap, zkeypath: String, callback: Callback) {
|
||||
Log.d(TAG, "inputs in provePassport kotlin: " + inputs.toString())
|
||||
|
||||
val mrz = inputs.getArray("mrz")?.toArrayList()?.map { it as String } ?: listOf()
|
||||
val reveal_bitmap = inputs.getArray("reveal_bitmap")?.toArrayList()?.map { it as String } ?: listOf()
|
||||
@@ -623,7 +627,16 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
|
||||
val pubkey = inputs.getArray("pubkey")?.toArrayList()?.map { it as String } ?: listOf()
|
||||
val address = inputs.getString("address") ?: ""
|
||||
|
||||
val resultFromProof = provePassport(mrz, reveal_bitmap, data_hashes, e_content_bytes, signature, pubkey, address)
|
||||
val resultFromProof = provePassport(
|
||||
mrz,
|
||||
reveal_bitmap,
|
||||
data_hashes,
|
||||
e_content_bytes,
|
||||
signature,
|
||||
pubkey,
|
||||
address,
|
||||
zkeypath
|
||||
)
|
||||
|
||||
Log.d(TAG, "resultFromProof: " + resultFromProof.toString())
|
||||
|
||||
@@ -631,6 +644,36 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
|
||||
callback.invoke(null, resultFromProof)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun downloadFile(url: String, fileName: String, promise: Promise) {
|
||||
val client = OkHttpClient()
|
||||
val request = Request.Builder().url(url).build()
|
||||
|
||||
try {
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Failed to download file: $response")
|
||||
|
||||
// Use the app's internal files directory
|
||||
val fileOutputStream = reactContext.openFileOutput(fileName, Context.MODE_PRIVATE)
|
||||
|
||||
val inputStream = response.body?.byteStream()
|
||||
inputStream.use { input ->
|
||||
fileOutputStream.use { output ->
|
||||
input?.copyTo(output)
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve the promise with the file path
|
||||
val file = File(reactContext.filesDir, fileName)
|
||||
promise.resolve(file.absolutePath)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Reject the promise if an exception occurs
|
||||
promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private val TAG = RNPassportReaderModule::class.java.simpleName
|
||||
private const val PARAM_DOC_NUM = "documentNumber";
|
||||
|
||||
587
app/ark-circom-passport/Cargo.lock
generated
587
app/ark-circom-passport/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -11,13 +11,16 @@ crate-type = ["cdylib"]
|
||||
# [profile.dev]
|
||||
# opt-level = 3
|
||||
|
||||
# XXX: Shouldn't be necessary, but this way we stay consistent with wasmer version and fix
|
||||
# error[E0432]: unresolved import `wasmer` error
|
||||
# (likely due to other packages)
|
||||
[patch.crates-io]
|
||||
wasmer = { git = "https://github.com/oskarth/wasmer.git", rev = "09c7070" }
|
||||
# NOTE: Forked wasmer to work around memory limits
|
||||
# See https://github.com/wasmerio/wasmer/commit/09c7070
|
||||
wasmer = { git = "https://github.com/oskarth/wasmer.git", rev = "09c7070" }
|
||||
|
||||
|
||||
[dependencies]
|
||||
wasmer = { git = "https://github.com/oskarth/wasmer.git", rev = "09c7070" }
|
||||
ark-circom = { git = "https://github.com/0xturboblitz/circom-compat.git" }
|
||||
ark-bn254 = { version = "=0.4.0" }
|
||||
ark-groth16 = { version = "=0.4.0", default-features = false, features = ["parallel"] }
|
||||
@@ -39,8 +42,8 @@ serde_derive = "1.0"
|
||||
byteorder = "=1.4.3"
|
||||
num-traits = { version = "=0.2.15", default-features = false }
|
||||
hex = "0.4"
|
||||
once_cell = "1.8"
|
||||
num-bigint = { version = "=0.4.3", default-features = false, features = [
|
||||
"rand",
|
||||
] }
|
||||
wasmer = { git = "https://github.com/oskarth/wasmer.git", rev = "09c7070" }
|
||||
once_cell = "1.8"
|
||||
ark-zkey = { git = "https://github.com/oskarth/mopro.git", branch = "main" }
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
|
||||
To run tests and see logs:
|
||||
```
|
||||
cargo test -- --nocapture
|
||||
cargo test --release -- --nocapture
|
||||
```
|
||||
|
||||
Could be replaced by mopro once mopro is available on Android.
|
||||
Could be replaced by mopro once mopro is available on Android.
|
||||
|
||||
To generate the arkzkey, if you have installed arkzkey-util globally from the mopro repo, you can run
|
||||
```
|
||||
cd passport
|
||||
arkzkey-util proof_of_passport_final.zkey
|
||||
```
|
||||
@@ -1,12 +1,15 @@
|
||||
use ark_circom::{ethereum, CircomBuilder, CircomConfig, circom::CircomReduction, WitnessCalculator};
|
||||
use ark_std::rand::thread_rng;
|
||||
use color_eyre::Result;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use ark_bn254::Bn254;
|
||||
use ark_crypto_primitives::snark::SNARK;
|
||||
use ark_groth16::{Groth16, Proof};
|
||||
use ark_ec::AffineRepr;
|
||||
use ark_ff::UniformRand;
|
||||
use ark_groth16::{ProvingKey};
|
||||
use ark_relations::r1cs::ConstraintMatrices;
|
||||
use ark_bn254::{Fr};
|
||||
|
||||
use num_bigint::{BigInt, Sign, ToBigInt};
|
||||
extern crate hex;
|
||||
@@ -32,7 +35,10 @@ use std::{
|
||||
collections::HashMap,
|
||||
time::Instant,
|
||||
convert::TryInto,
|
||||
sync::Mutex
|
||||
sync::Mutex,
|
||||
{os::raw::c_int, io::BufReader},
|
||||
fs::File,
|
||||
path::Path
|
||||
};
|
||||
|
||||
use wasmer::{Module, Store};
|
||||
@@ -41,6 +47,7 @@ use once_cell::sync::{Lazy, OnceCell};
|
||||
mod zkey;
|
||||
pub use zkey::{read_zkey, read_zkey_from_include_bytes};
|
||||
|
||||
use ark_zkey::{read_arkzkey, read_arkzkey_from_bytes};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_callRustCode(
|
||||
@@ -64,6 +71,29 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_callRustCode(
|
||||
output.into_inner()
|
||||
}
|
||||
|
||||
const WASM: &[u8] = include_bytes!("../passport/proof_of_passport.wasm");
|
||||
|
||||
static WITNESS_CALCULATOR: OnceCell<Mutex<WitnessCalculator>> = OnceCell::new();
|
||||
|
||||
#[cfg(not(feature = "dylib"))]
|
||||
#[must_use]
|
||||
pub fn witness_calculator() -> &'static Mutex<WitnessCalculator> {
|
||||
WITNESS_CALCULATOR.get_or_init(|| {
|
||||
let store = Store::default();
|
||||
let module = Module::from_binary(&store, WASM).expect("WASM should be valid");
|
||||
let result =
|
||||
WitnessCalculator::from_module(module).expect("Failed to create WitnessCalculator");
|
||||
Mutex::new(result)
|
||||
})
|
||||
}
|
||||
|
||||
fn load_arkzkey_from_file(zkey_path: &Path) -> Result<(ProvingKey<Bn254>, ConstraintMatrices<Fr>), Box<dyn std::error::Error>> {
|
||||
let file = File::open(zkey_path)?;
|
||||
let mut reader = BufReader::new(file);
|
||||
let (proving_key, matrices) = read_zkey(&mut reader)?;
|
||||
Ok((proving_key, matrices))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
|
||||
env: JNIEnv,
|
||||
@@ -75,9 +105,10 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
|
||||
signature: JObject,
|
||||
pubkey: JObject,
|
||||
address: JString,
|
||||
zkeypath: JString,
|
||||
) -> jstring {
|
||||
|
||||
log::error!("PROOF OF PASSPORT ---- formatting inputsaaaa...");
|
||||
log::error!("PROOF OF PASSPORT ---- formatting inputs...");
|
||||
|
||||
fn run_proof(
|
||||
mrz: JObject,
|
||||
reveal_bitmap: JObject,
|
||||
@@ -86,33 +117,18 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
|
||||
signature: JObject,
|
||||
pubkey: JObject,
|
||||
address: JString,
|
||||
zkeypath: JString,
|
||||
env: JNIEnv
|
||||
) -> Result<jstring, Box<dyn std::error::Error>> {
|
||||
android_logger::init_once(Config::default().with_min_level(Level::Trace));
|
||||
|
||||
|
||||
log::error!("PROOF OF PASSPORT ---- loading zkey...");
|
||||
let start = Instant::now();
|
||||
let now = Instant::now();
|
||||
let file_bytes: &'static [u8] = include_bytes!("../passport/proof_of_passport_final.zkey");
|
||||
log::error!("PROOF OF PASSPORT ---- zkey size: {}", file_bytes.len());
|
||||
let (params, matrices) = read_zkey_from_include_bytes(file_bytes).unwrap();
|
||||
log::error!("PROOF OF PASSPORT ---- zkey loaded. Took: {:?}", now.elapsed());
|
||||
let now = Instant::now();
|
||||
|
||||
println!("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!("PROOF OF PASSPORT ---- WASM and R1CS loaded. Took: {:?}", now.elapsed());
|
||||
let now = Instant::now();
|
||||
let cfg = CircomConfig::<Bn254>::from_bytes(MAIN_WASM, MAIN_R1CS)?;
|
||||
log::error!("PROOF OF PASSPORT ---- Circuit config: {:?}", now.elapsed());
|
||||
let now = Instant::now();
|
||||
android_logger::init_once(Config::default().with_min_level(Level::Trace));
|
||||
let mut rng = thread_rng();
|
||||
let rng = &mut rng;
|
||||
let r = ark_bn254::Fr::rand(rng);
|
||||
let s = ark_bn254::Fr::rand(rng);
|
||||
|
||||
log::error!("PROOF OF PASSPORT ---- formatting inputs...");
|
||||
log::error!("PROOF OF PASSPORT ---- mrz_veccccccc");
|
||||
|
||||
let mut builder = CircomBuilder::new(cfg,);
|
||||
let mut inputs: HashMap<String, Vec<num_bigint::BigInt>> = HashMap::new();
|
||||
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)?;
|
||||
let data_hashes_vec: Vec<String> = java_arraylist_to_rust_vec(&env, data_hashes)?;
|
||||
@@ -129,49 +145,95 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
|
||||
log::error!("PROOF OF PASSPORT ---- pubkey_vec {:?}", pubkey_vec);
|
||||
log::error!("PROOF OF PASSPORT ---- address_str {:?}", address_str);
|
||||
|
||||
mrz_vec.iter()
|
||||
.filter_map(|s| s.parse::<u128>().ok())
|
||||
.for_each(|n| builder.push_input("mrz", n));
|
||||
reveal_bitmap_vec.iter()
|
||||
.filter_map(|s| s.parse::<u128>().ok())
|
||||
.for_each(|n| builder.push_input("reveal_bitmap", n));
|
||||
data_hashes_vec.iter()
|
||||
.filter_map(|s| s.parse::<u128>().ok())
|
||||
.for_each(|n| builder.push_input("dataHashes", n));
|
||||
e_content_bytes_vec.iter()
|
||||
.filter_map(|s| s.parse::<u128>().ok())
|
||||
.for_each(|n| builder.push_input("eContentBytes", n));
|
||||
signature_vec.iter()
|
||||
.filter_map(|s| s.parse::<u128>().ok())
|
||||
.for_each(|n| builder.push_input("signature", n));
|
||||
pubkey_vec.iter()
|
||||
.filter_map(|s| s.parse::<u128>().ok())
|
||||
.for_each(|n| builder.push_input("pubkey", n));
|
||||
|
||||
fn parse_and_insert(hash_map: &mut HashMap<String, Vec<BigInt>>, key: &str, data: Vec<&str>) {
|
||||
let parsed_data: Vec<BigInt> = data.into_iter()
|
||||
.filter_map(|s| s.parse::<u128>().ok().and_then(|num| num.to_bigint()))
|
||||
.collect();
|
||||
hash_map.insert(key.to_string(), parsed_data);
|
||||
}
|
||||
|
||||
parse_and_insert(&mut inputs, "mrz", mrz_vec.iter().map(AsRef::as_ref).collect());
|
||||
parse_and_insert(&mut inputs, "reveal_bitmap", reveal_bitmap_vec.iter().map(AsRef::as_ref).collect());
|
||||
parse_and_insert(&mut inputs, "dataHashes", data_hashes_vec.iter().map(AsRef::as_ref).collect());
|
||||
parse_and_insert(&mut inputs, "eContentBytes", e_content_bytes_vec.iter().map(AsRef::as_ref).collect());
|
||||
parse_and_insert(&mut inputs, "signature", signature_vec.iter().map(AsRef::as_ref).collect());
|
||||
parse_and_insert(&mut inputs, "pubkey", pubkey_vec.iter().map(AsRef::as_ref).collect());
|
||||
|
||||
let address_bigint = BigInt::from_bytes_be(Sign::Plus, &decode(&address_str[2..])?);
|
||||
builder.push_input("address", address_bigint);
|
||||
|
||||
let circom = builder.build().unwrap(); // this here calculates the witness
|
||||
log::error!("PROOF OF PASSPORT ---- witness calculated. Took: {:?}", now.elapsed());
|
||||
let now = Instant::now();
|
||||
let inputs = circom.get_public_inputs().unwrap();
|
||||
inputs.insert("address".to_string(), vec![address_bigint]);
|
||||
|
||||
let mut rng = thread_rng();
|
||||
let proof = Groth16::<Bn254, CircomReduction>::prove(¶ms, circom, &mut rng).unwrap();
|
||||
println!("generating witness...");
|
||||
let now = Instant::now();
|
||||
let full_assignment = witness_calculator()
|
||||
.lock()
|
||||
.expect("Failed to lock witness calculator")
|
||||
.calculate_witness_element::<Bn254, _>(inputs, false)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
log::error!("PROOF OF PASSPORT ---- Witness generation took. Took: {:?}", now.elapsed());
|
||||
|
||||
log::error!("PROOF OF PASSPORT ---- loading zkey...");
|
||||
let now = Instant::now();
|
||||
let zkey_path_jstring = env.get_string(zkeypath).expect("Couldn't get zkey path string");
|
||||
let zkey_path_str = zkey_path_jstring.to_str().unwrap();
|
||||
|
||||
// To load classic zkey
|
||||
// let file = std::fs::File::open(zkey_path_str).expect("Failed to open zkey file");
|
||||
// let zkey = read_zkey(&mut BufReader::new(file)).expect("Failed to read zkey from file");
|
||||
|
||||
// Loading arkzkey
|
||||
let (serialized_proving_key, serialized_constraint_matrices) = read_arkzkey(zkey_path_str).expect("Failed to read zkey from file");
|
||||
// Formatting for ark-circom API here as it's not done in mopro rn
|
||||
let proving_key: ProvingKey<Bn254> = serialized_proving_key.0;
|
||||
let constraint_matrices: ConstraintMatrices<Fr> = ConstraintMatrices {
|
||||
num_instance_variables: serialized_constraint_matrices.num_instance_variables,
|
||||
num_witness_variables: serialized_constraint_matrices.num_witness_variables,
|
||||
num_constraints: serialized_constraint_matrices.num_constraints,
|
||||
a_num_non_zero: serialized_constraint_matrices.a_num_non_zero,
|
||||
b_num_non_zero: serialized_constraint_matrices.b_num_non_zero,
|
||||
c_num_non_zero: serialized_constraint_matrices.c_num_non_zero,
|
||||
a: serialized_constraint_matrices.a.data,
|
||||
b: serialized_constraint_matrices.b.data,
|
||||
c: serialized_constraint_matrices.c.data,
|
||||
};
|
||||
let zkey = (proving_key, constraint_matrices);
|
||||
|
||||
log::error!("PROOF OF PASSPORT ---- zkey loaded from path. Took: {:?}", now.elapsed());
|
||||
println!("Loading arkzkey took: {:.2?}", now.elapsed());
|
||||
let now = Instant::now();
|
||||
|
||||
let public_inputs = full_assignment.as_slice()[1..zkey.1.num_instance_variables].to_vec();
|
||||
let ark_proof = Groth16::<_, CircomReduction>::create_proof_with_reduction_and_matrices(
|
||||
&zkey.0,
|
||||
r,
|
||||
s,
|
||||
&zkey.1,
|
||||
zkey.1.num_instance_variables,
|
||||
zkey.1.num_constraints,
|
||||
full_assignment.as_slice(),
|
||||
);
|
||||
|
||||
let proof = ark_proof.map_err(|e| e.to_string())?;
|
||||
|
||||
log::error!("PROOF OF PASSPORT ---- proof: {:?}", proof);
|
||||
log::error!("PROOF OF PASSPORT ---- proof done. Took: {:?}", now.elapsed());
|
||||
let now = Instant::now();
|
||||
|
||||
let pvk = Groth16::<Bn254>::process_vk(¶ms.vk).unwrap();
|
||||
let verified = Groth16::<Bn254>::verify_with_processed_vk(&pvk, &inputs, &proof).unwrap();
|
||||
assert!(verified);
|
||||
println!("proof generation took: {:.2?}", now.elapsed());
|
||||
|
||||
println!("proof {:?}", proof);
|
||||
println!("public_inputs {:?}", public_inputs);
|
||||
|
||||
log::error!("PROOF OF PASSPORT ---- proof verified. Took: {:?}", now.elapsed());
|
||||
// previous way of verifying proof
|
||||
// let pvk = Groth16::<Bn254>::process_vk(¶ms.vk).unwrap();
|
||||
// let verified = Groth16::<Bn254>::verify_with_processed_vk(&pvk, &inputs, &proof).unwrap();
|
||||
// println!("Proof verified. Took: {:?}", now.elapsed());
|
||||
// log::error!("PROOF OF PASSPORT ---- proof verified. Took: {:?}", now.elapsed());
|
||||
// assert!(verified);
|
||||
|
||||
log::error!("PROOF OF PASSPORT ---- inputs: {:?}", inputs);
|
||||
log::error!("PROOF OF PASSPORT ---- public_inputs: {:?}", public_inputs);
|
||||
|
||||
let converted_inputs: ethereum::Inputs = inputs.as_slice().into();
|
||||
let converted_inputs: ethereum::Inputs = public_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!("PROOF OF PASSPORT ---- Serialized inputs: {:?}", serialized_inputs);
|
||||
@@ -201,6 +263,7 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
|
||||
signature,
|
||||
pubkey,
|
||||
address,
|
||||
zkeypath,
|
||||
env
|
||||
) {
|
||||
Ok(output) => output,
|
||||
@@ -259,14 +322,22 @@ mod tests {
|
||||
use std::{
|
||||
error::Error,
|
||||
fs::File,
|
||||
sync::Arc,
|
||||
sync::{Arc, Mutex},
|
||||
collections::HashMap
|
||||
};
|
||||
use wasmer::{Module, Store};
|
||||
|
||||
use ark_circom::{
|
||||
circom::CircomReduction,
|
||||
WitnessCalculator
|
||||
};
|
||||
use once_cell::sync::{Lazy, OnceCell};
|
||||
use ark_groth16::{Groth16, ProvingKey};
|
||||
use ark_relations::r1cs::ConstraintMatrices;
|
||||
use ark_ff::UniformRand;
|
||||
use ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G2Affine};
|
||||
use num_bigint::ToBigInt;
|
||||
use ark_zkey::read_arkzkey_from_bytes; //SerializableConstraintMatrices
|
||||
|
||||
|
||||
// We need to implement the conversion from the Ark-Circom's internal Ethereum types to
|
||||
@@ -334,18 +405,47 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
const WASM: &[u8] = include_bytes!("../passport/proof_of_passport.wasm");
|
||||
|
||||
static WITNESS_CALCULATOR: OnceCell<Mutex<WitnessCalculator>> = OnceCell::new();
|
||||
|
||||
#[cfg(not(feature = "dylib"))]
|
||||
#[must_use]
|
||||
pub fn witness_calculator() -> &'static Mutex<WitnessCalculator> {
|
||||
WITNESS_CALCULATOR.get_or_init(|| {
|
||||
let store = Store::default();
|
||||
let module = Module::from_binary(&store, WASM).expect("WASM should be valid");
|
||||
let result =
|
||||
WitnessCalculator::from_module(module).expect("Failed to create WitnessCalculator");
|
||||
Mutex::new(result)
|
||||
})
|
||||
}
|
||||
|
||||
const ARKZKEY_BYTES: &[u8] = include_bytes!("../passport/proof_of_passport_final.arkzkey");
|
||||
|
||||
static ARKZKEY: Lazy<(ProvingKey<Bn254>, ConstraintMatrices<Fr>)> = Lazy::new(|| {
|
||||
//let mut reader = Cursor::new(ARKZKEY_BYTES);
|
||||
// TODO: Use reader? More flexible; unclear if perf diff
|
||||
read_arkzkey_from_bytes(ARKZKEY_BYTES).expect("Failed to read arkzkey")
|
||||
});
|
||||
|
||||
// Experimental
|
||||
#[must_use]
|
||||
pub fn arkzkey() -> &'static (ProvingKey<Bn254>, ConstraintMatrices<Fr>) {
|
||||
&ARKZKEY
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_proof() -> Result<(), Box<dyn Error>> {
|
||||
let now = Instant::now();
|
||||
|
||||
println!("loading zkey...");
|
||||
let path = "./passport/proof_of_passport_final.zkey";
|
||||
let mut file = File::open(path).unwrap();
|
||||
let (params, matrices) = read_zkey(&mut file).unwrap();
|
||||
let mut rng = thread_rng();
|
||||
let rng = &mut rng;
|
||||
let r = ark_bn254::Fr::rand(rng);
|
||||
let s = ark_bn254::Fr::rand(rng);
|
||||
|
||||
let mut inputs: HashMap<String, Vec<num_bigint::BigInt>> = HashMap::new();
|
||||
let values = inputs.entry("a".to_string()).or_insert_with(Vec::new);
|
||||
values.push(3.into());
|
||||
|
||||
println!("Circuit loaded. Took: {:?}", now.elapsed());
|
||||
let now = Instant::now();
|
||||
|
||||
let mrz_vec: Vec<String> = vec!["97", "91", "95", "31", "88", "80", "60", "70", "82", "65", "84", "65", "86", "69", "82", "78", "73", "69", "82", "60", "60", "70", "76", "79", "82", "69", "78", "84", "60", "72", "85", "71", "85", "69", "83", "60", "74", "69", "65", "78", "60", "60", "60", "60", "60", "60", "60", "60", "60", "49", "57", "72", "65", "51", "52", "56", "50", "56", "52", "70", "82", "65", "48", "48", "48", "55", "49", "57", "49", "77", "50", "57", "49", "50", "48", "57", "53", "60", "60", "60", "60", "60", "60", "60", "60", "60", "60", "60", "60", "60", "60", "48", "50"].iter().map(|&s| s.to_string()).collect();
|
||||
let reveal_bitmap_vec: Vec<String> = vec!["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "1", "1", "1", "1", "1", "1", "1", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"].iter().map(|&s| s.to_string()).collect();
|
||||
let data_hashes_vec: Vec<String> = vec!["48", "130", "1", "37", "2", "1", "0", "48", "11", "6", "9", "96", "134", "72", "1", "101", "3", "4", "2", "1", "48", "130", "1", "17", "48", "37", "2", "1", "1", "4", "32", "99", "19", "179", "205", "55", "104", "45", "214", "133", "101", "233", "177", "130", "1", "37", "89", "125", "229", "139", "34", "132", "146", "28", "116", "248", "186", "63", "195", "96", "151", "26", "215", "48", "37", "2", "1", "2", "4", "32", "63", "234", "106", "78", "31", "16", "114", "137", "237", "17", "92", "71", "134", "47", "62", "78", "189", "233", "201", "213", "53", "4", "47", "189", "201", "133", "6", "121", "34", "131", "64", "142", "48", "37", "2", "1", "3", "4", "32", "136", "155", "87", "144", "121", "15", "152", "127", "85", "25", "154", "80", "20", "58", "51", "75", "193", "116", "234", "0", "60", "30", "29", "30", "183", "141", "72", "247", "255", "203", "100", "124", "48", "37", "2", "1", "11", "4", "32", "0", "194", "104", "108", "237", "246", "97", "230", "116", "198", "69", "110", "26", "87", "17", "89", "110", "199", "108", "250", "36", "21", "39", "87", "110", "102", "250", "213", "174", "131", "171", "174", "48", "37", "2", "1", "12", "4", "32", "190", "82", "180", "235", "222", "33", "79", "50", "152", "136", "142", "35", "116", "224", "6", "242", "156", "141", "128", "247", "10", "61", "98", "86", "248", "45", "207", "210", "90", "232", "175", "38", "48", "37", "2", "1", "13", "4", "32", "91", "222", "210", "193", "63", "222", "104", "82", "36", "41", "138", "253", "70", "15", "148", "208", "156", "45", "105", "171", "241", "195", "185", "43", "217", "162", "146", "201", "222", "89", "238", "38", "48", "37", "2", "1", "14", "4", "32", "76", "123", "216", "13", "52", "227", "72", "245", "59", "193", "238", "166", "103", "49", "24", "164", "171", "188", "194", "197", "156", "187", "249", "28", "198", "95", "69", "15", "182", "56", "54", "38"].iter().map(|&s| s.to_string()).collect();
|
||||
@@ -361,73 +461,58 @@ mod tests {
|
||||
hash_map.insert(key.to_string(), parsed_data);
|
||||
}
|
||||
|
||||
let mut inputs: HashMap<String, Vec<num_bigint::BigInt>> = HashMap::new();
|
||||
parse_and_insert(&mut inputs, "mrz", mrz_vec.iter().map(AsRef::as_ref).collect());
|
||||
parse_and_insert(&mut inputs, "reveal_bitmap", reveal_bitmap_vec.iter().map(AsRef::as_ref).collect());
|
||||
parse_and_insert(&mut inputs, "dataHashes", data_hashes_vec.iter().map(AsRef::as_ref).collect());
|
||||
parse_and_insert(&mut inputs, "eContentBytes", e_content_bytes_vec.iter().map(AsRef::as_ref).collect());
|
||||
parse_and_insert(&mut inputs, "signature", signature_vec.iter().map(AsRef::as_ref).collect());
|
||||
parse_and_insert(&mut inputs, "pubkey", pubkey_vec.iter().map(AsRef::as_ref).collect());
|
||||
|
||||
let address_bigint = BigInt::from_bytes_be(Sign::Plus, &decode(&address_str[2..])?);
|
||||
inputs.insert("address".to_string(), vec![address_bigint]);
|
||||
|
||||
let mut rng = thread_rng();
|
||||
use ark_std::UniformRand;
|
||||
let num_inputs = matrices.num_instance_variables;
|
||||
let num_constraints = matrices.num_constraints;
|
||||
let rng = &mut rng;
|
||||
|
||||
let r = ark_bn254::Fr::rand(rng);
|
||||
let s = ark_bn254::Fr::rand(rng);
|
||||
|
||||
let mut wtns = WitnessCalculator::new("./passport/proof_of_passport.wasm").unwrap();
|
||||
let full_assignment = wtns
|
||||
.calculate_witness_element::<Bn254, _>(inputs, false)
|
||||
.unwrap();
|
||||
println!("witness calculated. Took: {:?}", now.elapsed());
|
||||
println!("generating witness...");
|
||||
let now = Instant::now();
|
||||
let full_assignment = witness_calculator()
|
||||
.lock()
|
||||
.expect("Failed to lock witness calculator")
|
||||
.calculate_witness_element::<Bn254, _>(inputs, false)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
println!("Witness generation took: {:.2?}", now.elapsed());
|
||||
println!("loading circuit...");
|
||||
let now = Instant::now();
|
||||
let zkey = arkzkey();
|
||||
println!("Loading arkzkey took: {:.2?}", now.elapsed());
|
||||
|
||||
let proof = Groth16::<Bn254, CircomReduction>::create_proof_with_reduction_and_matrices(
|
||||
¶ms,
|
||||
let public_inputs = full_assignment.as_slice()[1..zkey.1.num_instance_variables].to_vec();
|
||||
let now = Instant::now();
|
||||
let ark_proof = Groth16::<_, CircomReduction>::create_proof_with_reduction_and_matrices(
|
||||
&zkey.0,
|
||||
r,
|
||||
s,
|
||||
&matrices,
|
||||
num_inputs,
|
||||
num_constraints,
|
||||
&zkey.1,
|
||||
zkey.1.num_instance_variables,
|
||||
zkey.1.num_constraints,
|
||||
full_assignment.as_slice(),
|
||||
)
|
||||
.unwrap();
|
||||
println!("proof done. Took: {:?}", now.elapsed());
|
||||
let now = Instant::now();
|
||||
);
|
||||
|
||||
let proof = ark_proof.map_err(|e| e.to_string())?;
|
||||
|
||||
println!("proof generation took: {:.2?}", now.elapsed());
|
||||
|
||||
println!("proof {:?}", proof);
|
||||
println!("public_inputs {:?}", public_inputs);
|
||||
|
||||
let pvk = Groth16::<Bn254>::process_vk(¶ms.vk).unwrap();
|
||||
let inputs = &full_assignment[1..num_inputs];
|
||||
let verified = Groth16::<Bn254>::verify_with_processed_vk(&pvk, inputs, &proof).unwrap();
|
||||
println!("proof verified. Took: {:?}", now.elapsed());
|
||||
|
||||
assert!(verified);
|
||||
|
||||
println!("inputs: {:?}", inputs);
|
||||
|
||||
let converted_inputs: ethereum::Inputs = inputs.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();
|
||||
println!("Serialized inputs: {:?}", serialized_inputs);
|
||||
|
||||
let proof_str = proof_to_proof_str(&proof);
|
||||
let serialized_proof = serde_json::to_string(&proof_str).unwrap();
|
||||
|
||||
println!("Serialized proof: {:?}", serialized_proof);
|
||||
|
||||
let combined = json!({
|
||||
"duration": now.elapsed().as_millis(),
|
||||
"serialized_proof": serialized_proof,
|
||||
"serialized_inputs": serialized_inputs
|
||||
});
|
||||
Ok(())
|
||||
// Ok((SerializableProof(proof), SerializableInputs(public_inputs)))
|
||||
// now = Instant::now();
|
||||
|
||||
let combined_str = combined.to_string();
|
||||
println!("proof and inputs: {:?}", combined_str);
|
||||
|
||||
// let pvk = Groth16::<Bn254>::process_vk(¶ms.vk).unwrap();
|
||||
// let verified = Groth16::<Bn254>::verify_with_processed_vk(&pvk, &inputs, &proof).unwrap();
|
||||
// println!("Proof verified. Took: {:?}", now.elapsed());
|
||||
|
||||
// assert!(verified);
|
||||
|
||||
// // launch the network & compile the verifier
|
||||
// println!("launching network");
|
||||
@@ -455,6 +540,6 @@ mod tests {
|
||||
|
||||
// assert!(onchain_verified);
|
||||
|
||||
Ok(())
|
||||
// Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,6 @@ cd android
|
||||
./gradlew cargoBuild
|
||||
cd ..
|
||||
|
||||
mkdir -p android/react-native-passport-reader/android/src/main/jniLibs/arm64/
|
||||
cp ark-circom-passport/target/aarch64-linux-android/release/libark_circom_passport.so android/react-native-passport-reader/android/src/main/jniLibs/arm64/
|
||||
mkdir -p android/react-native-passport-reader/android/src/main/jniLibs/arm64-v8a/
|
||||
cp ark-circom-passport/target/aarch64-linux-android/release/libark_circom_passport.so android/react-native-passport-reader/android/src/main/jniLibs/arm64-v8a/
|
||||
echo copied release version of android lib to android/
|
||||
@@ -20,7 +20,7 @@ interface MainScreenProps {
|
||||
address: string;
|
||||
setAddress: (address: string) => void;
|
||||
generatingProof: boolean;
|
||||
handleProve: () => void;
|
||||
handleProve: (path: string) => void;
|
||||
step: number;
|
||||
mintText: string;
|
||||
proof: any;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { NativeModules } from 'react-native';
|
||||
import { YStack, XStack, Text, Checkbox, Input, Button, Spinner, Image } from 'tamagui';
|
||||
import { Check, LayoutGrid, Scan } from '@tamagui/lucide-icons';
|
||||
import { getFirstName, formatDuration } from '../../utils/utils';
|
||||
@@ -10,6 +11,9 @@ import { App } from '../utils/AppClass';
|
||||
import { Keyboard, Platform } from 'react-native';
|
||||
const { ethers } = require('ethers');
|
||||
|
||||
const fileName = "passport.arkzkey"
|
||||
const path = "/data/user/0/com.proofofpassport/files/" + fileName
|
||||
|
||||
interface ProveScreenProps {
|
||||
selectedApp: App | null;
|
||||
passportData: any;
|
||||
@@ -18,7 +22,7 @@ interface ProveScreenProps {
|
||||
address: string;
|
||||
setAddress: (address: string) => void;
|
||||
generatingProof: boolean;
|
||||
handleProve: () => void;
|
||||
handleProve: (path: string) => void;
|
||||
handleMint: () => void;
|
||||
step: number;
|
||||
mintText: string;
|
||||
@@ -47,6 +51,26 @@ const ProveScreen: React.FC<ProveScreenProps> = ({
|
||||
ens,
|
||||
setEns
|
||||
}) => {
|
||||
const [zkeyLoaded, setZkeyLoaded] = useState(false);
|
||||
|
||||
const downloadZkey = async () => {
|
||||
// TODO: don't redownload if already in the file system at path, if downloaded from previous session
|
||||
|
||||
try {
|
||||
console.log('Downloading file...')
|
||||
const result = await NativeModules.RNPassportReader.downloadFile(
|
||||
'https://current-pop-zkey.s3.eu-north-1.amazonaws.com/proof_of_passport_final.arkzkey',
|
||||
fileName
|
||||
);
|
||||
console.log("Download successful");
|
||||
console.log(result);
|
||||
setZkeyLoaded(true);
|
||||
} catch (e: any) {
|
||||
console.log("Download not successful");
|
||||
console.error(e.message);
|
||||
}
|
||||
};
|
||||
|
||||
const maskString = (input: string): string => {
|
||||
if (input.length <= 5) {
|
||||
return input.charAt(0) + '*'.repeat(input.length - 1);
|
||||
@@ -70,7 +94,6 @@ const ProveScreen: React.FC<ProveScreenProps> = ({
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const resolveENS = async () => {
|
||||
if (inputValue != ens) {
|
||||
if (inputValue.endsWith('.eth')) {
|
||||
@@ -198,8 +221,11 @@ const ProveScreen: React.FC<ProveScreenProps> = ({
|
||||
<XStack f={1} />
|
||||
<XStack f={1} />
|
||||
<XStack f={1} />
|
||||
{(!keyboardVisible || Platform.OS == "ios") && <Button disabled={address == ethers.ZeroAddress} borderRadius={100} onPress={handleProve} mt="$8" backgroundColor={address == ethers.ZeroAddress ? "#cecece" : "#3185FC"} alignSelf='center' >
|
||||
{generatingProof ? (
|
||||
{(!keyboardVisible || Platform.OS == "ios") && <Button disabled={address == ethers.ZeroAddress} borderRadius={100} onPress={() => {!zkeyLoaded ? downloadZkey() : handleProve(path)}} mt="$8" backgroundColor={address == ethers.ZeroAddress ? "#cecece" : "#3185FC"} alignSelf='center' >
|
||||
|
||||
{!zkeyLoaded && Platform.OS != "ios" ? (
|
||||
<Text color="white" fow="bold">Download zkey</Text>
|
||||
) : generatingProof ? (
|
||||
<XStack ai="center">
|
||||
<Spinner />
|
||||
<Text color="white" marginLeft="$2" fow="bold" >Generating ZK proof</Text>
|
||||
@@ -207,6 +233,7 @@ const ProveScreen: React.FC<ProveScreenProps> = ({
|
||||
) : (
|
||||
<Text color="white" fow="bold">Generate ZK proof</Text>
|
||||
)}
|
||||
|
||||
</Button>}
|
||||
<Text fontSize={10} color={generatingProof ? "gray" : "white"} alignSelf='center'>This operation can take up to 2 mn</Text>
|
||||
<Text fontSize={9} color={generatingProof ? "gray" : "white"} pb="$2" alignSelf='center'>The application may freeze during this time (hard work)</Text>
|
||||
|
||||
@@ -61,6 +61,8 @@ contract ProofOfPassport is ERC721Enumerable, Ownable {
|
||||
uint256[2] memory c,
|
||||
uint256[16] memory inputs
|
||||
) public {
|
||||
require(verifier.verifyProof(a, b, c, inputs), "Invalid Proof");
|
||||
|
||||
// check that the nullifier has not been used before
|
||||
require(!nullifiers[inputs[3]], "Signature already nullified");
|
||||
|
||||
@@ -76,8 +78,6 @@ contract ProofOfPassport is ERC721Enumerable, Ownable {
|
||||
// commented out for now, since hard to keep up with all
|
||||
// require(cscaPubkey == inputs[], "Invalid pubkey in inputs");
|
||||
|
||||
require(verifier.verifyProof(a, b, c, inputs), "Invalid Proof");
|
||||
|
||||
// Effects: Mint token
|
||||
address addr = address(uint160(inputs[inputs.length - 1])); // generally the last one
|
||||
uint256 newTokenId = totalSupply();
|
||||
|
||||
Reference in New Issue
Block a user