iota_move_natives_latest/crypto/
ecvrf.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use 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    /// Base cost for invoking the `ecvrf_verify`
33    pub ecvrf_ecvrf_verify_cost_base: InternalGas,
34    ///  Cost per byte of `alpha_string`
35    pub ecvrf_ecvrf_verify_alpha_string_cost_per_byte: InternalGas,
36    ///  Cost per block of `alpha_string` with block size = 128
37    pub ecvrf_ecvrf_verify_alpha_string_cost_per_block: InternalGas,
38}
39/// ****************************************************************************
40/// ********************* native fun ecvrf_verify
41/// Implementation of the Move native function `ecvrf_verify(hash: &vector<u8>,
42/// alpha_string: &vector<u8>, public_key: &vector<u8>, proof: &vector<u8>):
43/// bool`   gas cost: ecvrf_ecvrf_verify_cost_base                    | covers
44/// various fixed costs in the oper
45///              + ecvrf_ecvrf_verify_alpha_string_cost_per_byte    *
46///                size_of(alpha_string)        | covers cost of operating on
47///                each byte of `alpha_string`
48///              + ecvrf_ecvrf_verify_alpha_string_cost_per_block   *
49///                num_blocks(alpha_string)     | covers cost of operating on
50///                each block in `alpha_string`
51/// Note: each block is of size `ECVRF_SHA512_BLOCK_SIZE` bytes, and we round
52/// up.       `hash`, `proof`, and `public_key` are fixed size, so their costs
53/// are included in the base cost. *********************************************
54/// **************************************************
55pub 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    // Load the cost parameters from the protocol config
64    let ecvrf_ecvrf_verify_cost_params = &context
65        .extensions()
66        .get::<NativesCostTable>()
67        .ecvrf_ecvrf_verify_cost_params
68        .clone();
69    // Charge the base cost for this oper
70    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    // Charge the arg size dependent costs
82    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}