iota_move_natives_latest/crypto/
poseidon.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, ops::Mul};
6
7use fastcrypto_zkp::bn254::poseidon::poseidon_bytes;
8use move_binary_format::errors::PartialVMResult;
9use move_core_types::{gas_algebra::InternalGas, vm_status::StatusCode};
10use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
11use move_vm_types::{
12    loaded_data::runtime_types::Type,
13    natives::function::{NativeResult, PartialVMError},
14    pop_arg,
15    values::{Value, VectorRef},
16};
17use smallvec::smallvec;
18
19use crate::{NativesCostTable, object_runtime::ObjectRuntime};
20
21pub const NON_CANONICAL_INPUT: u64 = 0;
22pub const NOT_SUPPORTED_ERROR: u64 = 1;
23
24fn is_supported(context: &NativeContext) -> bool {
25    context
26        .extensions()
27        .get::<ObjectRuntime>()
28        .protocol_config
29        .enable_poseidon()
30}
31
32#[derive(Clone)]
33pub struct PoseidonBN254CostParams {
34    /// Base cost for invoking the `poseidon_bn254` function
35    pub poseidon_bn254_cost_base: Option<InternalGas>,
36    /// Cost per block of `data`, where a block is 32 bytes
37    pub poseidon_bn254_data_cost_per_block: Option<InternalGas>,
38}
39
40/// ****************************************************************************
41/// ********************* native fun poseidon_bn254
42/// Implementation of the Move native function
43/// `poseidon::poseidon_bn254_internal(data: &vector<vector<u8>>): vector<u8>
44///   gas cost: poseidon_bn254_cost_base                           | base cost
45/// for function call and fixed opers
46///              + poseidon_bn254_data_cost_per_block * num_inputs | cost
47///                depends on number of inputs
48/// ****************************************************************************
49/// *******************
50pub fn poseidon_bn254_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 = &context
62        .extensions()
63        .get::<NativesCostTable>()
64        .poseidon_bn254_cost_params
65        .clone();
66
67    // Charge the base cost for this operation
68    native_charge_gas_early_exit!(
69        context,
70        cost_params
71            .poseidon_bn254_cost_base
72            .ok_or_else(
73                || PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
74                    .with_message("Gas cost for poseidon_bn254 not available".to_string())
75            )?
76    );
77
78    debug_assert!(ty_args.is_empty());
79    debug_assert!(args.len() == 1);
80
81    // The input is a reference to a vector of vector<u8>'s
82    let inputs = pop_arg!(args, VectorRef);
83
84    let length = inputs
85        .len(&Type::Vector(Box::new(Type::U8)))?
86        .value_as::<u64>()?;
87
88    // Charge the msg dependent costs
89    native_charge_gas_early_exit!(
90        context,
91        cost_params
92            .poseidon_bn254_data_cost_per_block
93            .ok_or_else(
94                || PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
95                    .with_message("Gas cost for poseidon_bn254 not available".to_string())
96            )?
97            .mul(length.into())
98    );
99
100    // Read the input vector
101    let field_elements = (0..length)
102        .map(|i| {
103            let reference = inputs.borrow_elem(i as usize, &Type::Vector(Box::new(Type::U8)))?;
104            let value = reference.value_as::<VectorRef>()?.as_bytes_ref().clone();
105            Ok(value)
106        })
107        .collect::<Result<Vec<_>, _>>()?;
108
109    match poseidon_bytes(&field_elements) {
110        Ok(result) => Ok(NativeResult::ok(
111            context.gas_used(),
112            smallvec![Value::vector_u8(result)],
113        )),
114        // This is also checked in the poseidon_bn254 move function but to be sure we handle it here
115        // also.
116        Err(_) => Ok(NativeResult::err(context.gas_used(), NON_CANONICAL_INPUT)),
117    }
118}