sui_move_natives_latest/crypto/
vdf.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3use crate::object_runtime::ObjectRuntime;
4use crate::{NativesCostTable, get_extension};
5use fastcrypto_vdf::class_group::QuadraticForm;
6use fastcrypto_vdf::class_group::discriminant::DISCRIMINANT_3072;
7use fastcrypto_vdf::vdf::VDF;
8use fastcrypto_vdf::vdf::wesolowski::DefaultVDF;
9use move_binary_format::errors::PartialVMResult;
10use move_core_types::gas_algebra::InternalGas;
11use move_core_types::vm_status::StatusCode;
12use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
13use move_vm_types::natives::function::PartialVMError;
14use move_vm_types::{
15    loaded_data::runtime_types::Type,
16    natives::function::NativeResult,
17    pop_arg,
18    values::{Value, VectorRef},
19};
20use smallvec::smallvec;
21use std::collections::VecDeque;
22
23pub const INVALID_INPUT_ERROR: u64 = 0;
24pub const NOT_SUPPORTED_ERROR: u64 = 1;
25
26fn is_supported(context: &NativeContext) -> PartialVMResult<bool> {
27    Ok(get_extension!(context, ObjectRuntime)?
28        .protocol_config
29        .enable_vdf())
30}
31
32#[derive(Clone)]
33pub struct VDFCostParams {
34    pub vdf_verify_cost: Option<InternalGas>,
35    pub hash_to_input_cost: Option<InternalGas>,
36}
37
38/***************************************************************************************************
39 * native fun vdf_verify_internal
40 *
41 * Implementation of the Move native function `vdf::verify_vdf_internal(
42 *      input: &vector<u8>,
43 *      output: &vector<u8>,
44 *      proof: &vector<u8>,
45 *      iterations: u64): bool`
46 *
47 * Gas cost: verify_vdf_cost
48 **************************************************************************************************/
49pub fn vdf_verify_internal(
50    context: &mut NativeContext,
51    ty_args: Vec<Type>,
52    mut args: VecDeque<Value>,
53) -> PartialVMResult<NativeResult> {
54    let cost = context.gas_used();
55    if !is_supported(context)? {
56        return Ok(NativeResult::err(cost, NOT_SUPPORTED_ERROR));
57    }
58
59    // Load the cost parameters from the protocol config
60    let cost_params = get_extension!(context, NativesCostTable)?
61        .vdf_cost_params
62        .clone();
63
64    // Charge the base cost for this operation
65    native_charge_gas_early_exit!(
66        context,
67        cost_params
68            .vdf_verify_cost
69            .ok_or_else(
70                || PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
71                    .with_message("Gas cost for vdf_verify not available".to_string())
72            )?
73    );
74
75    debug_assert!(ty_args.is_empty());
76    debug_assert!(args.len() == 4);
77
78    // The input is a reference to a vector of vector<u8>'s
79    let iterations = pop_arg!(args, u64);
80    let proof_bytes = pop_arg!(args, VectorRef);
81    let output_bytes = pop_arg!(args, VectorRef);
82    let input_bytes = pop_arg!(args, VectorRef);
83
84    let input = match bcs::from_bytes::<QuadraticForm>(&input_bytes.as_bytes_ref()) {
85        Ok(input) => input,
86        Err(_) => return Ok(NativeResult::err(context.gas_used(), INVALID_INPUT_ERROR)),
87    };
88
89    let proof = match bcs::from_bytes::<QuadraticForm>(&proof_bytes.as_bytes_ref()) {
90        Ok(proof) => proof,
91        Err(_) => return Ok(NativeResult::err(context.gas_used(), INVALID_INPUT_ERROR)),
92    };
93
94    let output = match bcs::from_bytes::<QuadraticForm>(&output_bytes.as_bytes_ref()) {
95        Ok(output) => output,
96        Err(_) => return Ok(NativeResult::err(context.gas_used(), INVALID_INPUT_ERROR)),
97    };
98
99    // We use the default VDF construction: Wesolowski's construction using a strong Fiat-Shamir
100    // construction and a windowed scalar multiplier to speed up the proof verification.
101    let vdf = DefaultVDF::new(DISCRIMINANT_3072.clone(), iterations);
102    let verified = vdf.verify(&input, &output, &proof).is_ok();
103
104    Ok(NativeResult::ok(
105        context.gas_used(),
106        smallvec![Value::bool(verified)],
107    ))
108}
109
110/***************************************************************************************************
111 * native fun hash_to_input_internal
112 *
113 * Implementation of the Move native function `vdf::hash_to_input_internal(message: &vector<u8>): vector<u8>`
114 *
115 * Gas cost: hash_to_input_cost
116 **************************************************************************************************/
117pub fn hash_to_input_internal(
118    context: &mut NativeContext,
119    ty_args: Vec<Type>,
120    mut args: VecDeque<Value>,
121) -> PartialVMResult<NativeResult> {
122    let cost = context.gas_used();
123    if !is_supported(context)? {
124        return Ok(NativeResult::err(cost, NOT_SUPPORTED_ERROR));
125    }
126
127    // Load the cost parameters from the protocol config
128    let cost_params = get_extension!(context, NativesCostTable)?
129        .vdf_cost_params
130        .clone();
131
132    // Charge the base cost for this operation
133    native_charge_gas_early_exit!(
134        context,
135        cost_params
136            .hash_to_input_cost
137            .ok_or_else(
138                || PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
139                    .with_message("Gas cost for hash_to_input not available".to_string())
140            )?
141    );
142
143    debug_assert!(ty_args.is_empty());
144    debug_assert!(args.len() == 1);
145
146    let message = pop_arg!(args, VectorRef);
147
148    let output = match QuadraticForm::hash_to_group_with_default_parameters(
149        &message.as_bytes_ref(),
150        &DISCRIMINANT_3072,
151    ) {
152        Ok(output) => output,
153        Err(_) => return Ok(NativeResult::err(context.gas_used(), INVALID_INPUT_ERROR)),
154    };
155
156    let output_bytes = match bcs::to_bytes(&output) {
157        Ok(bytes) => bytes,
158        // This should only fail on extremely large inputs, so we treat it as an invalid input error
159        Err(_) => return Ok(NativeResult::err(context.gas_used(), INVALID_INPUT_ERROR)),
160    };
161
162    Ok(NativeResult::ok(
163        context.gas_used(),
164        smallvec![Value::vector_u8(output_bytes)],
165    ))
166}