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;
101    if curve == BLS12381 {
102        result = fastcrypto_zkp::bls12381::api::prepare_pvk_bytes(&verifying_key);
103    } else if curve == BN254 {
104        result = fastcrypto_zkp::bn254::api::prepare_pvk_bytes(&verifying_key);
105    } else {
106        return Ok(NativeResult::err(cost, INVALID_CURVE));
107    }
108
109    match result {
110        Ok(pvk) => Ok(NativeResult::ok(
111            cost,
112            smallvec![Value::struct_(values::Struct::pack(vec![
113                Value::vector_u8(pvk[0].to_vec()),
114                Value::vector_u8(pvk[1].to_vec()),
115                Value::vector_u8(pvk[2].to_vec()),
116                Value::vector_u8(pvk[3].to_vec())
117            ]))],
118        )),
119        Err(_) => Ok(NativeResult::err(cost, INVALID_VERIFYING_KEY)),
120    }
121}
122
123#[derive(Clone)]
124pub struct Groth16VerifyGroth16ProofInternalCostParams {
125    pub groth16_verify_groth16_proof_internal_bls12381_cost_base: InternalGas,
126    pub groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: InternalGas,
127
128    pub groth16_verify_groth16_proof_internal_bn254_cost_base: InternalGas,
129    pub groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: InternalGas,
130
131    pub groth16_verify_groth16_proof_internal_public_input_cost_per_byte: InternalGas,
132}
133/***************************************************************************************************
134 * native fun verify_groth16_proof_internal
135 * Implementation of the Move native function `verify_groth16_proof_internal(curve: u8, vk_gamma_abc_g1_bytes: &vector<u8>,
136 *                          alpha_g1_beta_g2_bytes: &vector<u8>, gamma_g2_neg_pc_bytes: &vector<u8>, delta_g2_neg_pc_bytes: &vector<u8>,
137 *                          public_proof_inputs: &vector<u8>, proof_points: &vector<u8>): bool`
138 *
139 * This function has two cost modes depending on the curve being set to `BLS12381` or `BN254`. The core formula is same but constants differ.
140 * If curve = 0, we use the `bls12381` cost constants, otherwise we use the `bn254` cost constants.
141 *   gas cost: groth16_prepare_verifying_key_cost_base                    | covers various fixed costs in the oper
142 *              + groth16_verify_groth16_proof_internal_public_input_cost_per_byte
143 *                                                   * size_of(public_proof_inputs) | covers the cost of verifying each public input per byte
144 *              + groth16_verify_groth16_proof_internal_cost_per_public_input
145 *                                                   * num_public_inputs) | covers the cost of verifying each public input per input
146 * Note: every other arg is fixed size, so their costs are included in the base cost.
147 **************************************************************************************************/
148pub fn verify_groth16_proof_internal(
149    context: &mut NativeContext,
150    ty_args: Vec<Type>,
151    mut args: VecDeque<Value>,
152) -> PartialVMResult<NativeResult> {
153    debug_assert!(ty_args.is_empty());
154    debug_assert!(args.len() == 7);
155
156    // Load the cost parameters from the protocol config
157    let (groth16_verify_groth16_proof_internal_cost_params, crypto_invalid_arguments_cost) = {
158        let cost_table: &NativesCostTable = get_extension!(context)?;
159        (
160            cost_table
161                .groth16_verify_groth16_proof_internal_cost_params
162                .clone(),
163            cost_table.crypto_invalid_arguments_cost,
164        )
165    };
166    let bytes5 = pop_arg!(args, VectorRef);
167    let proof_points = bytes5.as_bytes_ref()?;
168
169    let bytes4 = pop_arg!(args, VectorRef);
170    let public_proof_inputs = bytes4.as_bytes_ref()?;
171
172    let bytes3 = pop_arg!(args, VectorRef);
173    let delta_g2_neg_pc = bytes3.as_bytes_ref()?;
174
175    let bytes2 = pop_arg!(args, VectorRef);
176    let gamma_g2_neg_pc = bytes2.as_bytes_ref()?;
177
178    let byte1 = pop_arg!(args, VectorRef);
179    let alpha_g1_beta_g2 = byte1.as_bytes_ref()?;
180
181    let bytes = pop_arg!(args, VectorRef);
182    let vk_gamma_abc_g1 = bytes.as_bytes_ref()?;
183
184    let curve = pop_arg!(args, u8);
185
186    let (base_cost, cost_per_public_input, num_public_inputs) = match curve {
187        BLS12381 => (
188            groth16_verify_groth16_proof_internal_cost_params
189                .groth16_verify_groth16_proof_internal_bls12381_cost_base,
190            groth16_verify_groth16_proof_internal_cost_params
191                .groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input,
192            public_proof_inputs
193                .len()
194                .div_ceil(fastcrypto::groups::bls12381::SCALAR_LENGTH),
195        ),
196        BN254 => (
197            groth16_verify_groth16_proof_internal_cost_params
198                .groth16_verify_groth16_proof_internal_bn254_cost_base,
199            groth16_verify_groth16_proof_internal_cost_params
200                .groth16_verify_groth16_proof_internal_bn254_cost_per_public_input,
201            public_proof_inputs
202                .len()
203                .div_ceil(fastcrypto_zkp::bn254::api::SCALAR_SIZE),
204        ),
205        _ => {
206            // Charge for failure but dont fail if we run out of gas otherwise the actual error is masked by OUT_OF_GAS error
207            context.charge_gas(crypto_invalid_arguments_cost)?;
208            let cost = if get_extension!(context, ObjectRuntime)?
209                .protocol_config
210                .native_charging_v2()
211            {
212                context.gas_used()
213            } else {
214                context.gas_budget()
215            };
216            return Ok(NativeResult::err(cost, INVALID_CURVE));
217        }
218    };
219    // Charge the base cost for this oper
220    native_charge_gas_early_exit!(context, base_cost);
221    // Charge the arg size dependent costs
222    native_charge_gas_early_exit!(
223        context,
224        cost_per_public_input * (num_public_inputs as u64).into()
225            + groth16_verify_groth16_proof_internal_cost_params
226                .groth16_verify_groth16_proof_internal_public_input_cost_per_byte
227                * (public_proof_inputs.len() as u64).into()
228    );
229
230    let cost = context.gas_used();
231
232    let result;
233    if curve == BLS12381 {
234        if public_proof_inputs.len()
235            > fastcrypto::groups::bls12381::SCALAR_LENGTH * MAX_PUBLIC_INPUTS
236        {
237            return Ok(NativeResult::err(cost, TOO_MANY_PUBLIC_INPUTS));
238        }
239        result = fastcrypto_zkp::bls12381::api::verify_groth16_in_bytes(
240            &vk_gamma_abc_g1,
241            &alpha_g1_beta_g2,
242            &gamma_g2_neg_pc,
243            &delta_g2_neg_pc,
244            &public_proof_inputs,
245            &proof_points,
246        );
247    } else if curve == BN254 {
248        if public_proof_inputs.len() > fastcrypto_zkp::bn254::api::SCALAR_SIZE * MAX_PUBLIC_INPUTS {
249            return Ok(NativeResult::err(cost, TOO_MANY_PUBLIC_INPUTS));
250        }
251        result = fastcrypto_zkp::bn254::api::verify_groth16_in_bytes(
252            &vk_gamma_abc_g1,
253            &alpha_g1_beta_g2,
254            &gamma_g2_neg_pc,
255            &delta_g2_neg_pc,
256            &public_proof_inputs,
257            &proof_points,
258        );
259    } else {
260        return Ok(NativeResult::err(cost, INVALID_CURVE));
261    }
262
263    Ok(NativeResult::ok(
264        cost,
265        smallvec![Value::bool(result.unwrap_or(false))],
266    ))
267}