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