Skip to main content

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