iota_move_natives_latest/crypto/
poseidon.rs1use 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 pub poseidon_bn254_cost_base: Option<InternalGas>,
36 pub poseidon_bn254_data_cost_per_block: Option<InternalGas>,
38}
39
40pub 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 let cost_params = &context
62 .extensions()
63 .get::<NativesCostTable>()
64 .poseidon_bn254_cost_params
65 .clone();
66
67 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 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 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 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 Err(_) => Ok(NativeResult::err(context.gas_used(), NON_CANONICAL_INPUT)),
117 }
118}