iota_move_natives_latest/crypto/
vdf.rs1use std::collections::VecDeque;
6
7use fastcrypto_vdf::{
8 class_group::{QuadraticForm, discriminant::DISCRIMINANT_3072},
9 vdf::{VDF, wesolowski::DefaultVDF},
10};
11use move_binary_format::errors::PartialVMResult;
12use move_core_types::{gas_algebra::InternalGas, vm_status::StatusCode};
13use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
14use move_vm_types::{
15 loaded_data::runtime_types::Type,
16 natives::function::{NativeResult, PartialVMError},
17 pop_arg,
18 values::{Value, VectorRef},
19};
20use smallvec::smallvec;
21
22use crate::{NativesCostTable, object_runtime::ObjectRuntime};
23
24pub const INVALID_INPUT_ERROR: u64 = 0;
25pub const NOT_SUPPORTED_ERROR: u64 = 1;
26
27fn is_supported(context: &NativeContext) -> bool {
28 context
29 .extensions()
30 .get::<ObjectRuntime>()
31 .protocol_config
32 .enable_vdf()
33}
34
35#[derive(Clone)]
36pub struct VDFCostParams {
37 pub vdf_verify_cost: Option<InternalGas>,
38 pub hash_to_input_cost: Option<InternalGas>,
39}
40
41pub fn vdf_verify_internal(
54 context: &mut NativeContext,
55 ty_args: Vec<Type>,
56 mut args: VecDeque<Value>,
57) -> PartialVMResult<NativeResult> {
58 let cost = context.gas_used();
59 if !is_supported(context) {
60 return Ok(NativeResult::err(cost, NOT_SUPPORTED_ERROR));
61 }
62
63 let cost_params = &context
65 .extensions()
66 .get::<NativesCostTable>()
67 .vdf_cost_params
68 .clone();
69
70 native_charge_gas_early_exit!(
72 context,
73 cost_params
74 .vdf_verify_cost
75 .ok_or_else(
76 || PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
77 .with_message("Gas cost for vdf_verify not available".to_string())
78 )?
79 );
80
81 debug_assert!(ty_args.is_empty());
82 debug_assert!(args.len() == 4);
83
84 let iterations = pop_arg!(args, u64);
86 let proof_bytes = pop_arg!(args, VectorRef);
87 let output_bytes = pop_arg!(args, VectorRef);
88 let input_bytes = pop_arg!(args, VectorRef);
89
90 let input = match bcs::from_bytes::<QuadraticForm>(&input_bytes.as_bytes_ref()) {
91 Ok(input) => input,
92 Err(_) => return Ok(NativeResult::err(context.gas_used(), INVALID_INPUT_ERROR)),
93 };
94
95 let proof = match bcs::from_bytes::<QuadraticForm>(&proof_bytes.as_bytes_ref()) {
96 Ok(proof) => proof,
97 Err(_) => return Ok(NativeResult::err(context.gas_used(), INVALID_INPUT_ERROR)),
98 };
99
100 let output = match bcs::from_bytes::<QuadraticForm>(&output_bytes.as_bytes_ref()) {
101 Ok(output) => output,
102 Err(_) => return Ok(NativeResult::err(context.gas_used(), INVALID_INPUT_ERROR)),
103 };
104
105 let vdf = DefaultVDF::new(DISCRIMINANT_3072.clone(), iterations);
109 let verified = vdf.verify(&input, &output, &proof).is_ok();
110
111 Ok(NativeResult::ok(
112 context.gas_used(),
113 smallvec![Value::bool(verified)],
114 ))
115}
116
117pub fn hash_to_input_internal(
127 context: &mut NativeContext,
128 ty_args: Vec<Type>,
129 mut args: VecDeque<Value>,
130) -> PartialVMResult<NativeResult> {
131 let cost = context.gas_used();
132 if !is_supported(context) {
133 return Ok(NativeResult::err(cost, NOT_SUPPORTED_ERROR));
134 }
135
136 let cost_params = &context
138 .extensions()
139 .get::<NativesCostTable>()
140 .vdf_cost_params
141 .clone();
142
143 native_charge_gas_early_exit!(
145 context,
146 cost_params
147 .hash_to_input_cost
148 .ok_or_else(
149 || PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
150 .with_message("Gas cost for hash_to_input not available".to_string())
151 )?
152 );
153
154 debug_assert!(ty_args.is_empty());
155 debug_assert!(args.len() == 1);
156
157 let message = pop_arg!(args, VectorRef);
158
159 let output = match QuadraticForm::hash_to_group_with_default_parameters(
160 &message.as_bytes_ref(),
161 &DISCRIMINANT_3072,
162 ) {
163 Ok(output) => output,
164 Err(_) => return Ok(NativeResult::err(context.gas_used(), INVALID_INPUT_ERROR)),
165 };
166
167 let output_bytes = match bcs::to_bytes(&output) {
168 Ok(bytes) => bytes,
169 Err(_) => return Ok(NativeResult::err(context.gas_used(), INVALID_INPUT_ERROR)),
171 };
172
173 Ok(NativeResult::ok(
174 context.gas_used(),
175 smallvec![Value::vector_u8(output_bytes)],
176 ))
177}