Skip to main content

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