Skip to content

Commit

Permalink
feat(core,ffi): convert proof and input for smart contract
Browse files Browse the repository at this point in the history
  • Loading branch information
vivianjeng committed Jan 18, 2024
1 parent 04a990d commit 2645e07
Show file tree
Hide file tree
Showing 7 changed files with 312 additions and 2 deletions.
9 changes: 9 additions & 0 deletions mopro-core/src/middleware/circom/serialization.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use ark_bn254::Bn254;
use ark_circom::ethereum;
use ark_ec::pairing::Pairing;
use ark_groth16::{Proof, ProvingKey};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
Expand Down Expand Up @@ -49,6 +50,14 @@ pub fn deserialize_inputs(data: Vec<u8>) -> SerializableInputs {
SerializableInputs::deserialize_uncompressed(&mut &data[..]).expect("Deserialization failed")
}

pub fn convert_proof(proof: &SerializableProof) -> ethereum::Proof {
ethereum::Proof::from(proof.0.clone())
}

pub fn convert_inputs(inputs: &SerializableInputs) -> ethereum::Inputs {
ethereum::Inputs::from(&inputs.0[..])
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
59 changes: 57 additions & 2 deletions mopro-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,25 @@ pub struct GenerateProofResult {
pub inputs: Vec<u8>,
}

#[derive(Debug, Clone)]
pub struct G1 {
pub x: String,
pub y: String,
}

#[derive(Debug, Clone)]
pub struct G2 {
pub x: Vec<String>,
pub y: Vec<String>,
}

#[derive(Debug, Clone)]
pub struct ProofCalldata {
pub a: G1,
pub b: G2,
pub c: G1,
}

// NOTE: Make UniFFI and Rust happy, can maybe do some renaming here
#[allow(non_snake_case)]
#[derive(Debug, Clone)]
Expand Down Expand Up @@ -104,6 +123,30 @@ pub fn verify_proof2(proof: Vec<u8>, public_input: Vec<u8>) -> Result<bool, Mopr
Ok(is_valid)
}

pub fn convert_proof(proof: Vec<u8>) -> ProofCalldata {
let deserialized_proof = circom::serialization::deserialize_proof(proof);
let proof = circom::serialization::convert_proof(&deserialized_proof);
let a = G1 {
x: proof.a.x.to_string(),
y: proof.a.y.to_string()
};
let b = G2 {
x: proof.b.x.iter().map(|x| x.to_string()).collect(),
y: proof.b.y.iter().map(|x| x.to_string()).collect(),
};
let c = G1 {
x: proof.c.x.to_string(),
y: proof.c.y.to_string()
};
ProofCalldata { a, b, c }
}

pub fn convert_inputs(inputs: Vec<u8>) -> Vec<String> {
let deserialized_inputs = circom::serialization::deserialize_inputs(inputs);
let inputs = deserialized_inputs.0.iter().map(|x| x.to_string()).collect();
inputs
}

// TODO: Use FFIError::SerializationError instead
impl MoproCircom {
pub fn new() -> Self {
Expand Down Expand Up @@ -242,9 +285,15 @@ mod tests {
assert_eq!(serialized_inputs, serialized_outputs);

// Step 3: Verify Proof
let is_valid = mopro_circom.verify_proof(serialized_proof, serialized_inputs)?;
let is_valid = mopro_circom.verify_proof(serialized_proof.clone(), serialized_inputs.clone())?;
assert!(is_valid);

// Step 4: Convert Proof
let proof_calldata = convert_proof(serialized_proof);
let inputs_calldata = convert_inputs(serialized_inputs);
assert!(proof_calldata.a.x.len() > 0);
assert!(inputs_calldata.len() > 0);

Ok(())
}

Expand Down Expand Up @@ -287,9 +336,15 @@ mod tests {

// Step 3: Verify Proof

let is_valid = mopro_circom.verify_proof(serialized_proof, serialized_inputs)?;
let is_valid = mopro_circom.verify_proof(serialized_proof.clone(), serialized_inputs.clone())?;
assert!(is_valid);

// Step 4: Convert Proof
let proof_calldata = convert_proof(serialized_proof);
let inputs_calldata = convert_inputs(serialized_inputs);
assert!(proof_calldata.a.x.len() > 0);
assert!(inputs_calldata.len() > 0);

Ok(())
}
}
20 changes: 20 additions & 0 deletions mopro-ffi/src/mopro.udl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ namespace mopro {

[Throws=MoproError]
boolean verify_proof2(bytes proof, bytes public_input);

ProofCalldata convert_proof(bytes proof);
sequence<string> convert_inputs(bytes inputs);
};

dictionary SetupResult {
Expand All @@ -24,6 +27,23 @@ dictionary GenerateProofResult {
bytes inputs;
};

dictionary G1 {
string x;
string y;
};

dictionary G2 {
sequence<string> x;
sequence<string> y;
};

dictionary ProofCalldata {
G1 a;
G2 b;
G1 c;
};


[Error]
enum MoproError {
"CircomError",
Expand Down
13 changes: 13 additions & 0 deletions mopro-ffi/tests/bindings/test_mopro.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,31 @@ var wasmPath = "../mopro-core/examples/circom/multiplier2/target/multiplier2_js/
var r1csPath = "../mopro-core/examples/circom/multiplier2/target/multiplier2.r1cs"

try {
// Setup
var moproCircom = MoproCircom()
var setupResult = moproCircom.setup(wasmPath, r1csPath)
assert(setupResult.provingKey.size > 0) { "Proving key should not be empty" }

// Prepare inputs
val inputs = mutableMapOf<String, List<String>>()
inputs["a"] = listOf("3")
inputs["b"] = listOf("5")

// Generate proof
var generateProofResult = moproCircom.generateProof(inputs)
assert(generateProofResult.proof.size > 0) { "Proof is empty" }

// Verify proof
var isValid = moproCircom.verifyProof(generateProofResult.proof, generateProofResult.inputs)
assert(isValid) { "Proof is invalid" }

// Convert proof
var convertProofResult = convertProof(generateProofResult.proof)
var convertInputsResult = convertInputs(generateProofResult.inputs)
assert(convertProofResult.a.x.isNotEmpty()) { "Proof is empty" }
assert(convertInputsResult.size > 0) { "Inputs are empty" }


} catch (e: Exception) {
println(e)
}
6 changes: 6 additions & 0 deletions mopro-ffi/tests/bindings/test_mopro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ do {
let isValid = try moproCircom.verifyProof(proof: generateProofResult.proof, publicInput: generateProofResult.inputs)
assert(isValid, "Proof verification should succeed")

// Convert Proof
let convertProofResult = convertProof(proof: generateProofResult.proof)
let convertInputsResult = convertInputs(inputs: generateProofResult.inputs)
assert(convertProofResult.a.x.count > 0, "Proof should not be empty")
assert(convertInputsResult.count > 0, "Inputs should not be empty")

} catch let error as MoproError {
print("MoproError: \(error)")
} catch {
Expand Down
Loading

0 comments on commit 2645e07

Please sign in to comment.