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