forked from rooch-network/rooch
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[rooch-networkgh-1644] add groth16 verification support.
- Loading branch information
Feliciss
committed
Jul 13, 2024
1 parent
58ec3e6
commit c3fe20e
Showing
10 changed files
with
764 additions
and
39 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// Copyright (c) RoochNetwork | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
/// Source from https://github.com/MystenLabs/sui/blob/924c294d9b4a98d5bc50cd6c830e7c0cdbc2a2b1/crates/sui-framework/packages/sui-framework/sources/crypto/groth16.move | ||
module moveos_std::groth16 { | ||
|
||
#[allow(unused_const)] | ||
// Error for input is not a valid Arkwork representation of a verifying key. | ||
const EInvalidVerifyingKey: u64 = 0; | ||
|
||
#[allow(unused_const)] | ||
// Error if the given curve is not supported | ||
const EInvalidCurve: u64 = 1; | ||
|
||
#[allow(unused_const)] | ||
// Error if the number of public inputs given exceeds the max. | ||
const ETooManyPublicInputs: u64 = 2; | ||
|
||
/// Represents an elliptic curve construction to be used in the verifier. Currently we support BLS12-381 and BN254. | ||
/// This should be given as the first parameter to `prepare_verifying_key` or `verify_groth16_proof`. | ||
public struct Curve has store, copy, drop { | ||
id: u8, | ||
} | ||
|
||
/// Return the `Curve` value indicating that the BLS12-381 construction should be used in a given function. | ||
public fun bls12381(): Curve { Curve { id: 0 } } | ||
|
||
/// Return the `Curve` value indicating that the BN254 construction should be used in a given function. | ||
public fun bn254(): Curve { Curve { id: 1 } } | ||
|
||
/// A `PreparedVerifyingKey` consisting of four components in serialized form. | ||
public struct PreparedVerifyingKey has store, copy, drop { | ||
vk_gamma_abc_g1_bytes: vector<u8>, | ||
alpha_g1_beta_g2_bytes: vector<u8>, | ||
gamma_g2_neg_pc_bytes: vector<u8>, | ||
delta_g2_neg_pc_bytes: vector<u8> | ||
} | ||
|
||
/// Creates a `PreparedVerifyingKey` from bytes. | ||
public fun pvk_from_bytes(vk_gamma_abc_g1_bytes: vector<u8>, alpha_g1_beta_g2_bytes: vector<u8>, gamma_g2_neg_pc_bytes: vector<u8>, delta_g2_neg_pc_bytes: vector<u8>): PreparedVerifyingKey { | ||
PreparedVerifyingKey { | ||
vk_gamma_abc_g1_bytes, | ||
alpha_g1_beta_g2_bytes, | ||
gamma_g2_neg_pc_bytes, | ||
delta_g2_neg_pc_bytes | ||
} | ||
} | ||
|
||
/// Returns bytes of the four components of the `PreparedVerifyingKey`. | ||
public fun pvk_to_bytes(pvk: PreparedVerifyingKey): vector<vector<u8>> { | ||
vector[ | ||
pvk.vk_gamma_abc_g1_bytes, | ||
pvk.alpha_g1_beta_g2_bytes, | ||
pvk.gamma_g2_neg_pc_bytes, | ||
pvk.delta_g2_neg_pc_bytes, | ||
] | ||
} | ||
|
||
/// A `PublicProofInputs` wrapper around its serialized bytes. | ||
public struct PublicProofInputs has store, copy, drop { | ||
bytes: vector<u8>, | ||
} | ||
|
||
/// Creates a `PublicProofInputs` wrapper from bytes. | ||
public fun public_proof_inputs_from_bytes(bytes: vector<u8>): PublicProofInputs { | ||
PublicProofInputs { bytes } | ||
} | ||
|
||
/// A `ProofPoints` wrapper around the serialized form of three proof points. | ||
public struct ProofPoints has store, copy, drop { | ||
bytes: vector<u8> | ||
} | ||
|
||
/// Creates a Groth16 `ProofPoints` from bytes. | ||
public fun proof_points_from_bytes(bytes: vector<u8>): ProofPoints { | ||
ProofPoints { bytes } | ||
} | ||
|
||
/// @param curve: What elliptic curve construction to use. See `bls12381` and `bn254`. | ||
/// @param verifying_key: An Arkworks canonical compressed serialization of a verifying key. | ||
/// | ||
/// Returns four vectors of bytes representing the four components of a prepared verifying key. | ||
/// This step computes one pairing e(P, Q), and binds the verification to one particular proof statement. | ||
/// This can be used as inputs for the `verify_groth16_proof` function. | ||
public fun prepare_verifying_key(curve: &Curve, verifying_key: &vector<u8>): PreparedVerifyingKey { | ||
prepare_verifying_key_internal(curve.id, verifying_key) | ||
} | ||
|
||
/// Native functions that flattens the inputs into an array and passes to the Rust native function. May abort with `EInvalidVerifyingKey` or `EInvalidCurve`. | ||
native fun prepare_verifying_key_internal(curve: u8, verifying_key: &vector<u8>): PreparedVerifyingKey; | ||
|
||
/// @param curve: What elliptic curve construction to use. See the `bls12381` and `bn254` functions. | ||
/// @param prepared_verifying_key: Consists of four vectors of bytes representing the four components of a prepared verifying key. | ||
/// @param public_proof_inputs: Represent inputs that are public. | ||
/// @param proof_points: Represent three proof points. | ||
/// | ||
/// Returns a boolean indicating whether the proof is valid. | ||
public fun verify_groth16_proof(curve: &Curve, prepared_verifying_key: &PreparedVerifyingKey, public_proof_inputs: &PublicProofInputs, proof_points: &ProofPoints): bool { | ||
verify_groth16_proof_internal( | ||
curve.id, | ||
&prepared_verifying_key.vk_gamma_abc_g1_bytes, | ||
&prepared_verifying_key.alpha_g1_beta_g2_bytes, | ||
&prepared_verifying_key.gamma_g2_neg_pc_bytes, | ||
&prepared_verifying_key.delta_g2_neg_pc_bytes, | ||
&public_proof_inputs.bytes, | ||
&proof_points.bytes | ||
) | ||
} | ||
|
||
/// Native functions that flattens the inputs into arrays of vectors and passed to the Rust native function. May abort with `EInvalidCurve` or `ETooManyPublicInputs`. | ||
native fun verify_groth16_proof_internal(curve: u8, vk_gamma_abc_g1_bytes: &vector<u8>, alpha_g1_beta_g2_bytes: &vector<u8>, gamma_g2_neg_pc_bytes: &vector<u8>, delta_g2_neg_pc_bytes: &vector<u8>, public_proof_inputs: &vector<u8>, proof_points: &vector<u8>): bool; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
240 changes: 240 additions & 0 deletions
240
frameworks/moveos-stdlib/src/natives/moveos_stdlib/groth16.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
// Copyright (c) RoochNetwork | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use move_binary_format::errors::PartialVMResult; | ||
use move_core_types::gas_algebra::{InternalGas, InternalGasPerByte, NumBytes}; | ||
use move_vm_runtime::native_functions::{NativeContext, NativeFunction}; | ||
use move_vm_types::{ | ||
loaded_data::runtime_types::Type, | ||
natives::function::NativeResult, | ||
pop_arg, | ||
values::{self, Value, VectorRef}, | ||
}; | ||
use smallvec::smallvec; | ||
use std::collections::VecDeque; | ||
|
||
use crate::natives::helpers::{make_module_natives, make_native}; | ||
|
||
pub const INVALID_CURVE: u64 = 0; | ||
pub const INVALID_VERIFYING_KEY: u64 = 1; | ||
pub const TOO_MANY_PUBLIC_INPUTS: u64 = 2; | ||
|
||
// These must match the corresponding values in sui::groth16::Curve. | ||
pub const BLS12381: u8 = 0; | ||
pub const BN254: u8 = 1; | ||
|
||
// We need to set an upper bound on the number of public inputs to avoid a DoS attack | ||
pub const MAX_PUBLIC_INPUTS: usize = 8; | ||
|
||
/*************************************************************************************************** | ||
* native fun prepare_verifying_key_internal | ||
* Implementation of the Move native function `prepare_verifying_key_internal(curve: u8, verifying_key: &vector<u8>): PreparedVerifyingKey` | ||
* This function has two cost modes depending on the curve being set to `BLS12381` or `BN254`. The core formula is same but constants differ. | ||
* If curve = 0, we use the `bls12381` cost constants, otherwise we use the `bn254` cost constants. | ||
* gas cost: groth16_prepare_verifying_key_cost_base | covers various fixed costs in the oper | ||
* Note: `curve` and `verifying_key` are fixed size, so their costs are included in the base cost. | ||
**************************************************************************************************/ | ||
pub fn native_prepare_verifying_key_internal( | ||
gas_params: &FromBytesGasParameters, | ||
_context: &mut NativeContext, | ||
ty_args: Vec<Type>, | ||
mut args: VecDeque<Value>, | ||
) -> PartialVMResult<NativeResult> { | ||
debug_assert!(ty_args.is_empty()); | ||
debug_assert!(args.len() == 2); | ||
|
||
let verifying_key = pop_arg!(args, VectorRef); | ||
let curve = pop_arg!(args, u8); | ||
|
||
let verifying_key_bytes_ref = verifying_key.as_bytes_ref(); | ||
|
||
let base_cost = match curve { | ||
BLS12381 => { | ||
52 // TODO: use variable? | ||
} | ||
BN254 => { | ||
52 // TODO: use variable? | ||
} | ||
_ => { | ||
return Ok(NativeResult::err(gas_params.base, INVALID_CURVE)); | ||
} | ||
}; | ||
|
||
let result; | ||
if curve == BLS12381 { | ||
result = fastcrypto_zkp::bls12381::api::prepare_pvk_bytes(&verifying_key_bytes_ref); | ||
} else if curve == BN254 { | ||
result = fastcrypto_zkp::bn254::api::prepare_pvk_bytes(&verifying_key_bytes_ref); | ||
} else { | ||
return Ok(NativeResult::err(base_cost.into(), INVALID_CURVE)); | ||
} | ||
|
||
match result { | ||
Ok(pvk) => Ok(NativeResult::ok( | ||
base_cost.into(), | ||
smallvec![Value::struct_(values::Struct::pack(vec![ | ||
Value::vector_u8(pvk[0].to_vec()), | ||
Value::vector_u8(pvk[1].to_vec()), | ||
Value::vector_u8(pvk[2].to_vec()), | ||
Value::vector_u8(pvk[3].to_vec()) | ||
]))], | ||
)), | ||
Err(_) => Ok(NativeResult::err(base_cost.into(), INVALID_VERIFYING_KEY)), | ||
} | ||
} | ||
|
||
/*************************************************************************************************** | ||
* native fun verify_groth16_proof_internal | ||
* Implementation of the Move native function `verify_groth16_proof_internal(curve: u8, vk_gamma_abc_g1_bytes: &vector<u8>, | ||
* alpha_g1_beta_g2_bytes: &vector<u8>, gamma_g2_neg_pc_bytes: &vector<u8>, delta_g2_neg_pc_bytes: &vector<u8>, | ||
* public_proof_inputs: &vector<u8>, proof_points: &vector<u8>): bool` | ||
* | ||
* This function has two cost modes depending on the curve being set to `BLS12381` or `BN254`. The core formula is same but constants differ. | ||
* If curve = 0, we use the `bls12381` cost constants, otherwise we use the `bn254` cost constants. | ||
* gas cost: groth16_prepare_verifying_key_cost_base | covers various fixed costs in the oper | ||
* + groth16_verify_groth16_proof_internal_public_input_cost_per_byte | ||
* * size_of(public_proof_inputs) | covers the cost of verifying each public input per byte | ||
* + groth16_verify_groth16_proof_internal_cost_per_public_input | ||
* * num_public_inputs) | covers the cost of verifying each public input per input | ||
* Note: every other arg is fixed size, so their costs are included in the base cost. | ||
**************************************************************************************************/ | ||
pub fn native_verify_groth16_proof_internal( | ||
gas_params: &FromBytesGasParameters, | ||
_context: &mut NativeContext, | ||
ty_args: Vec<Type>, | ||
mut args: VecDeque<Value>, | ||
) -> PartialVMResult<NativeResult> { | ||
debug_assert!(ty_args.is_empty()); | ||
debug_assert!(args.len() == 7); | ||
|
||
let proof_points = pop_arg!(args, VectorRef); | ||
let public_proof_inputs = pop_arg!(args, VectorRef); | ||
let delta_g2_neg_pc = pop_arg!(args, VectorRef); | ||
let gamma_g2_neg_pc = pop_arg!(args, VectorRef); | ||
let alpha_g1_beta_g2 = pop_arg!(args, VectorRef); | ||
let vk_gamma_abc_g1 = pop_arg!(args, VectorRef); | ||
let curve = pop_arg!(args, u8); | ||
|
||
let proof_points_bytes_ref = proof_points.as_bytes_ref(); | ||
let public_proof_inputs_bytes_ref = public_proof_inputs.as_bytes_ref(); | ||
let delta_g2_neg_pc_bytes_ref = delta_g2_neg_pc.as_bytes_ref(); | ||
let gamma_g2_neg_pc_bytes_ref = gamma_g2_neg_pc.as_bytes_ref(); | ||
let alpha_g1_beta_g2_bytes_ref = alpha_g1_beta_g2.as_bytes_ref(); | ||
let vk_gamma_abc_g1_bytes_ref = vk_gamma_abc_g1.as_bytes_ref(); | ||
|
||
let (base_cost, cost_per_public_input, num_public_inputs) = match curve { | ||
BLS12381 => ( | ||
52, // TODO: use variable? | ||
2, // TODO: use variable? | ||
(public_proof_inputs_bytes_ref.len() | ||
+ fastcrypto_zkp::bls12381::conversions::SCALAR_SIZE | ||
- 1) | ||
/ fastcrypto_zkp::bls12381::conversions::SCALAR_SIZE, | ||
), | ||
BN254 => ( | ||
52, // TODO: use variable? | ||
2, // TODO: use variable? | ||
(public_proof_inputs_bytes_ref.len() + fastcrypto_zkp::bn254::api::SCALAR_SIZE - 1) | ||
/ fastcrypto_zkp::bn254::api::SCALAR_SIZE, | ||
), | ||
_ => { | ||
return Ok(NativeResult::err(gas_params.base, INVALID_CURVE)); | ||
} | ||
}; | ||
|
||
let cost = (gas_params.per_byte * NumBytes::new(public_proof_inputs_bytes_ref.len() as u64)) | ||
+ (cost_per_public_input * num_public_inputs as u64).into() | ||
+ base_cost.into(); | ||
|
||
let result; | ||
if curve == BLS12381 { | ||
if public_proof_inputs_bytes_ref.len() | ||
> fastcrypto_zkp::bls12381::conversions::SCALAR_SIZE * MAX_PUBLIC_INPUTS | ||
{ | ||
return Ok(NativeResult::err(cost, TOO_MANY_PUBLIC_INPUTS)); | ||
} | ||
result = fastcrypto_zkp::bls12381::api::verify_groth16_in_bytes( | ||
&vk_gamma_abc_g1_bytes_ref, | ||
&alpha_g1_beta_g2_bytes_ref, | ||
&gamma_g2_neg_pc_bytes_ref, | ||
&delta_g2_neg_pc_bytes_ref, | ||
&public_proof_inputs_bytes_ref, | ||
&proof_points_bytes_ref, | ||
); | ||
} else if curve == BN254 { | ||
if public_proof_inputs_bytes_ref.len() | ||
> fastcrypto_zkp::bn254::api::SCALAR_SIZE * MAX_PUBLIC_INPUTS | ||
{ | ||
return Ok(NativeResult::err(cost, TOO_MANY_PUBLIC_INPUTS)); | ||
} | ||
result = fastcrypto_zkp::bn254::api::verify_groth16_in_bytes( | ||
&vk_gamma_abc_g1_bytes_ref, | ||
&alpha_g1_beta_g2_bytes_ref, | ||
&gamma_g2_neg_pc_bytes_ref, | ||
&delta_g2_neg_pc_bytes_ref, | ||
&public_proof_inputs_bytes_ref, | ||
&proof_points_bytes_ref, | ||
); | ||
} else { | ||
return Ok(NativeResult::err(cost, INVALID_CURVE)); | ||
} | ||
|
||
Ok(NativeResult::ok( | ||
cost, | ||
smallvec![Value::bool(result.unwrap_or(false))], | ||
)) | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct FromBytesGasParameters { | ||
pub base: InternalGas, | ||
pub per_byte: InternalGasPerByte, | ||
} | ||
|
||
impl FromBytesGasParameters { | ||
pub fn zeros() -> Self { | ||
Self { | ||
base: 0.into(), | ||
per_byte: 0.into(), | ||
} | ||
} | ||
} | ||
|
||
/*************************************************************************************************** | ||
* module | ||
**************************************************************************************************/ | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct GasParameters { | ||
pub prepare_verifying_key_internal: FromBytesGasParameters, | ||
pub verify_groth16_proof_internal: FromBytesGasParameters, | ||
} | ||
|
||
impl GasParameters { | ||
pub fn zeros() -> Self { | ||
Self { | ||
prepare_verifying_key_internal: FromBytesGasParameters::zeros(), | ||
verify_groth16_proof_internal: FromBytesGasParameters::zeros(), | ||
} | ||
} | ||
} | ||
|
||
pub fn make_all(gas_params: GasParameters) -> impl Iterator<Item = (String, NativeFunction)> { | ||
let natives = [ | ||
( | ||
"prepare_verifying_key_internal", | ||
make_native( | ||
gas_params.prepare_verifying_key_internal, | ||
native_prepare_verifying_key_internal, | ||
), | ||
), | ||
( | ||
"verify_groth16_proof_internal", | ||
make_native( | ||
gas_params.verify_groth16_proof_internal, | ||
native_verify_groth16_proof_internal, | ||
), | ||
), | ||
]; | ||
make_module_natives(natives) | ||
} |
Oops, something went wrong.