sui_move_natives_latest/crypto/
groth16.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3use crate::{NativesCostTable, get_extension, object_runtime::ObjectRuntime};
4use move_binary_format::errors::PartialVMResult;
5use move_core_types::gas_algebra::InternalGas;
6use move_vm_runtime::{
7    execution::{
8        Type,
9        values::{self, Value, VectorRef},
10    },
11    natives::functions::NativeResult,
12    pop_arg,
13};
14use move_vm_runtime::{native_charge_gas_early_exit, natives::functions::NativeContext};
15use smallvec::smallvec;
16use std::collections::VecDeque;
17
18pub const INVALID_VERIFYING_KEY: u64 = 0;
19pub const INVALID_CURVE: u64 = 1;
20pub const TOO_MANY_PUBLIC_INPUTS: u64 = 2;
21
22// These must match the corresponding values in sui::groth16::Curve.
23pub const BLS12381: u8 = 0;
24pub const BN254: u8 = 1;
25
26// We need to set an upper bound on the number of public inputs to avoid a DoS attack
27pub const MAX_PUBLIC_INPUTS: usize = 8;
28
29#[derive(Clone)]
30pub struct Groth16PrepareVerifyingKeyCostParams {
31    pub groth16_prepare_verifying_key_bls12381_cost_base: InternalGas,
32    pub groth16_prepare_verifying_key_bn254_cost_base: InternalGas,
33}
34/***************************************************************************************************
35 * native fun prepare_verifying_key_internal
36 * Implementation of the Move native function `prepare_verifying_key_internal(curve: u8, verifying_key: &vector<u8>): PreparedVerifyingKey`
37 * This function has two cost modes depending on the curve being set to `BLS12381` or `BN254`. The core formula is same but constants differ.
38 * If curve = 0, we use the `bls12381` cost constants, otherwise we use the `bn254` cost constants.
39 *   gas cost: groth16_prepare_verifying_key_cost_base                    | covers various fixed costs in the oper
40 * Note: `curve` and `verifying_key` are fixed size, so their costs are included in the base cost.
41 **************************************************************************************************/
42pub fn prepare_verifying_key_internal(
43    context: &mut NativeContext,
44    ty_args: Vec<Type>,
45    mut args: VecDeque<Value>,
46) -> PartialVMResult<NativeResult> {
47    debug_assert!(ty_args.is_empty());
48    debug_assert!(args.len() == 2);
49
50    // Load the cost parameters from the protocol config
51    let (groth16_prepare_verifying_key_cost_params, crypto_invalid_arguments_cost) = {
52        let cost_table: &NativesCostTable = get_extension!(context)?;
53        (
54            cost_table.groth16_prepare_verifying_key_cost_params.clone(),
55            cost_table.crypto_invalid_arguments_cost,
56        )
57    };
58    let bytes = pop_arg!(args, VectorRef);
59    let verifying_key = bytes.as_bytes_ref()?;
60
61    let curve = pop_arg!(args, u8);
62
63    // Load the cost parameters from the protocol config
64    let base_cost = match curve {
65        BLS12381 => {
66            groth16_prepare_verifying_key_cost_params
67                .groth16_prepare_verifying_key_bls12381_cost_base
68        }
69        BN254 => {
70            groth16_prepare_verifying_key_cost_params.groth16_prepare_verifying_key_bn254_cost_base
71        }
72        _ => {
73            context.charge_gas(crypto_invalid_arguments_cost)?;
74            return Ok(NativeResult::err(context.gas_used(), INVALID_CURVE));
75        }
76    };
77    // Charge the base cost for this oper
78    native_charge_gas_early_exit!(context, base_cost);
79    let cost = context.gas_used();
80
81    let result;
82    if curve == BLS12381 {
83        result = fastcrypto_zkp::bls12381::api::prepare_pvk_bytes(&verifying_key);
84    } else if curve == BN254 {
85        result = fastcrypto_zkp::bn254::api::prepare_pvk_bytes(&verifying_key);
86    } else {
87        return Ok(NativeResult::err(cost, INVALID_CURVE));
88    }
89
90    match result {
91        Ok(pvk) => Ok(NativeResult::ok(
92            cost,
93            smallvec![Value::struct_(values::Struct::pack(vec![
94                Value::vector_u8(pvk[0].to_vec()),
95                Value::vector_u8(pvk[1].to_vec()),
96                Value::vector_u8(pvk[2].to_vec()),
97                Value::vector_u8(pvk[3].to_vec())
98            ]))],
99        )),
100        Err(_) => Ok(NativeResult::err(cost, INVALID_VERIFYING_KEY)),
101    }
102}
103
104#[derive(Clone)]
105pub struct Groth16VerifyGroth16ProofInternalCostParams {
106    pub groth16_verify_groth16_proof_internal_bls12381_cost_base: InternalGas,
107    pub groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: InternalGas,
108
109    pub groth16_verify_groth16_proof_internal_bn254_cost_base: InternalGas,
110    pub groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: InternalGas,
111
112    pub groth16_verify_groth16_proof_internal_public_input_cost_per_byte: InternalGas,
113}
114/***************************************************************************************************
115 * native fun verify_groth16_proof_internal
116 * Implementation of the Move native function `verify_groth16_proof_internal(curve: u8, vk_gamma_abc_g1_bytes: &vector<u8>,
117 *                          alpha_g1_beta_g2_bytes: &vector<u8>, gamma_g2_neg_pc_bytes: &vector<u8>, delta_g2_neg_pc_bytes: &vector<u8>,
118 *                          public_proof_inputs: &vector<u8>, proof_points: &vector<u8>): bool`
119 *
120 * This function has two cost modes depending on the curve being set to `BLS12381` or `BN254`. The core formula is same but constants differ.
121 * If curve = 0, we use the `bls12381` cost constants, otherwise we use the `bn254` cost constants.
122 *   gas cost: groth16_prepare_verifying_key_cost_base                    | covers various fixed costs in the oper
123 *              + groth16_verify_groth16_proof_internal_public_input_cost_per_byte
124 *                                                   * size_of(public_proof_inputs) | covers the cost of verifying each public input per byte
125 *              + groth16_verify_groth16_proof_internal_cost_per_public_input
126 *                                                   * num_public_inputs) | covers the cost of verifying each public input per input
127 * Note: every other arg is fixed size, so their costs are included in the base cost.
128 **************************************************************************************************/
129pub fn verify_groth16_proof_internal(
130    context: &mut NativeContext,
131    ty_args: Vec<Type>,
132    mut args: VecDeque<Value>,
133) -> PartialVMResult<NativeResult> {
134    debug_assert!(ty_args.is_empty());
135    debug_assert!(args.len() == 7);
136
137    // Load the cost parameters from the protocol config
138    let (groth16_verify_groth16_proof_internal_cost_params, crypto_invalid_arguments_cost) = {
139        let cost_table: &NativesCostTable = get_extension!(context)?;
140        (
141            cost_table
142                .groth16_verify_groth16_proof_internal_cost_params
143                .clone(),
144            cost_table.crypto_invalid_arguments_cost,
145        )
146    };
147    let bytes5 = pop_arg!(args, VectorRef);
148    let proof_points = bytes5.as_bytes_ref()?;
149
150    let bytes4 = pop_arg!(args, VectorRef);
151    let public_proof_inputs = bytes4.as_bytes_ref()?;
152
153    let bytes3 = pop_arg!(args, VectorRef);
154    let delta_g2_neg_pc = bytes3.as_bytes_ref()?;
155
156    let bytes2 = pop_arg!(args, VectorRef);
157    let gamma_g2_neg_pc = bytes2.as_bytes_ref()?;
158
159    let byte1 = pop_arg!(args, VectorRef);
160    let alpha_g1_beta_g2 = byte1.as_bytes_ref()?;
161
162    let bytes = pop_arg!(args, VectorRef);
163    let vk_gamma_abc_g1 = bytes.as_bytes_ref()?;
164
165    let curve = pop_arg!(args, u8);
166
167    let (base_cost, cost_per_public_input, num_public_inputs) = match curve {
168        BLS12381 => (
169            groth16_verify_groth16_proof_internal_cost_params
170                .groth16_verify_groth16_proof_internal_bls12381_cost_base,
171            groth16_verify_groth16_proof_internal_cost_params
172                .groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input,
173            public_proof_inputs
174                .len()
175                .div_ceil(fastcrypto::groups::bls12381::SCALAR_LENGTH),
176        ),
177        BN254 => (
178            groth16_verify_groth16_proof_internal_cost_params
179                .groth16_verify_groth16_proof_internal_bn254_cost_base,
180            groth16_verify_groth16_proof_internal_cost_params
181                .groth16_verify_groth16_proof_internal_bn254_cost_per_public_input,
182            public_proof_inputs
183                .len()
184                .div_ceil(fastcrypto_zkp::bn254::api::SCALAR_SIZE),
185        ),
186        _ => {
187            // Charge for failure but dont fail if we run out of gas otherwise the actual error is masked by OUT_OF_GAS error
188            context.charge_gas(crypto_invalid_arguments_cost)?;
189            let cost = if get_extension!(context, ObjectRuntime)?
190                .protocol_config
191                .native_charging_v2()
192            {
193                context.gas_used()
194            } else {
195                context.gas_budget()
196            };
197            return Ok(NativeResult::err(cost, INVALID_CURVE));
198        }
199    };
200    // Charge the base cost for this oper
201    native_charge_gas_early_exit!(context, base_cost);
202    // Charge the arg size dependent costs
203    native_charge_gas_early_exit!(
204        context,
205        cost_per_public_input * (num_public_inputs as u64).into()
206            + groth16_verify_groth16_proof_internal_cost_params
207                .groth16_verify_groth16_proof_internal_public_input_cost_per_byte
208                * (public_proof_inputs.len() as u64).into()
209    );
210
211    let cost = context.gas_used();
212
213    let result;
214    if curve == BLS12381 {
215        if public_proof_inputs.len()
216            > fastcrypto::groups::bls12381::SCALAR_LENGTH * MAX_PUBLIC_INPUTS
217        {
218            return Ok(NativeResult::err(cost, TOO_MANY_PUBLIC_INPUTS));
219        }
220        result = fastcrypto_zkp::bls12381::api::verify_groth16_in_bytes(
221            &vk_gamma_abc_g1,
222            &alpha_g1_beta_g2,
223            &gamma_g2_neg_pc,
224            &delta_g2_neg_pc,
225            &public_proof_inputs,
226            &proof_points,
227        );
228    } else if curve == BN254 {
229        if public_proof_inputs.len() > fastcrypto_zkp::bn254::api::SCALAR_SIZE * MAX_PUBLIC_INPUTS {
230            return Ok(NativeResult::err(cost, TOO_MANY_PUBLIC_INPUTS));
231        }
232        result = fastcrypto_zkp::bn254::api::verify_groth16_in_bytes(
233            &vk_gamma_abc_g1,
234            &alpha_g1_beta_g2,
235            &gamma_g2_neg_pc,
236            &delta_g2_neg_pc,
237            &public_proof_inputs,
238            &proof_points,
239        );
240    } else {
241        return Ok(NativeResult::err(cost, INVALID_CURVE));
242    }
243
244    Ok(NativeResult::ok(
245        cost,
246        smallvec![Value::bool(result.unwrap_or(false))],
247    ))
248}