iota_move_natives_latest/crypto/
ecvrf.rs1use std::collections::VecDeque;
6
7use fastcrypto::vrf::{
8 VRFProof,
9 ecvrf::{ECVRFProof, ECVRFPublicKey},
10};
11use move_binary_format::errors::PartialVMResult;
12use move_core_types::gas_algebra::InternalGas;
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,
17 pop_arg,
18 values::{Value, VectorRef},
19};
20use smallvec::smallvec;
21
22use crate::NativesCostTable;
23
24pub const INVALID_ECVRF_HASH_LENGTH: u64 = 1;
25pub const INVALID_ECVRF_PUBLIC_KEY: u64 = 2;
26pub const INVALID_ECVRF_PROOF: u64 = 3;
27
28const ECVRF_SHA512_BLOCK_SIZE: usize = 128;
29
30#[derive(Clone)]
31pub struct EcvrfEcvrfVerifyCostParams {
32 pub ecvrf_ecvrf_verify_cost_base: InternalGas,
34 pub ecvrf_ecvrf_verify_alpha_string_cost_per_byte: InternalGas,
36 pub ecvrf_ecvrf_verify_alpha_string_cost_per_block: InternalGas,
38}
39pub fn ecvrf_verify(
56 context: &mut NativeContext,
57 ty_args: Vec<Type>,
58 mut args: VecDeque<Value>,
59) -> PartialVMResult<NativeResult> {
60 debug_assert!(ty_args.is_empty());
61 debug_assert!(args.len() == 4);
62
63 let ecvrf_ecvrf_verify_cost_params = &context
65 .extensions()
66 .get::<NativesCostTable>()
67 .ecvrf_ecvrf_verify_cost_params
68 .clone();
69 native_charge_gas_early_exit!(
71 context,
72 ecvrf_ecvrf_verify_cost_params.ecvrf_ecvrf_verify_cost_base
73 );
74
75 let proof_bytes = pop_arg!(args, VectorRef);
76 let public_key_bytes = pop_arg!(args, VectorRef);
77 let alpha_string = pop_arg!(args, VectorRef);
78 let hash_bytes = pop_arg!(args, VectorRef);
79
80 let alpha_string_len = alpha_string.as_bytes_ref().len();
81 native_charge_gas_early_exit!(
83 context,
84 ecvrf_ecvrf_verify_cost_params.ecvrf_ecvrf_verify_alpha_string_cost_per_byte
85 * (alpha_string_len as u64).into()
86 + ecvrf_ecvrf_verify_cost_params.ecvrf_ecvrf_verify_alpha_string_cost_per_block
87 * (alpha_string_len.div_ceil(ECVRF_SHA512_BLOCK_SIZE) as u64).into()
88 );
89
90 let cost = context.gas_used();
91
92 let Ok(hash) = hash_bytes.as_bytes_ref().as_slice().try_into() else {
93 return Ok(NativeResult::err(cost, INVALID_ECVRF_HASH_LENGTH));
94 };
95
96 let Ok(public_key) =
97 bcs::from_bytes::<ECVRFPublicKey>(public_key_bytes.as_bytes_ref().as_slice())
98 else {
99 return Ok(NativeResult::err(cost, INVALID_ECVRF_PUBLIC_KEY));
100 };
101
102 let Ok(proof) = bcs::from_bytes::<ECVRFProof>(proof_bytes.as_bytes_ref().as_slice()) else {
103 return Ok(NativeResult::err(cost, INVALID_ECVRF_PROOF));
104 };
105
106 let result = proof.verify_output(alpha_string.as_bytes_ref().as_slice(), &public_key, &hash);
107 Ok(NativeResult::ok(
108 cost,
109 smallvec![Value::bool(result.is_ok())],
110 ))
111}