1use crate::object_runtime::ObjectRuntime;
4use crate::{NativesCostTable, get_extension};
5use fastcrypto::error::{FastCryptoError, FastCryptoResult};
6use fastcrypto::groups::{
7 FromTrustedByteArray, GroupElement, HashToGroupElement, MultiScalarMul, Pairing,
8 bls12381 as bls,
9};
10use fastcrypto::serde_helpers::ToFromByteArray;
11use move_binary_format::errors::{PartialVMError, PartialVMResult};
12use move_core_types::gas_algebra::InternalGas;
13use move_core_types::vm_status::StatusCode;
14use move_vm_runtime::native_charge_gas_early_exit;
15use move_vm_runtime::native_functions::NativeContext;
16use move_vm_types::{
17 loaded_data::runtime_types::Type,
18 natives::function::NativeResult,
19 pop_arg,
20 values::{Value, VectorRef},
21};
22use smallvec::smallvec;
23use std::collections::VecDeque;
24
25pub const NOT_SUPPORTED_ERROR: u64 = 0;
26pub const INVALID_INPUT_ERROR: u64 = 1;
27pub const INPUT_TOO_LONG_ERROR: u64 = 2;
28
29fn is_supported(context: &NativeContext) -> PartialVMResult<bool> {
30 Ok(get_extension!(context, ObjectRuntime)?
31 .protocol_config
32 .enable_group_ops_native_functions())
33}
34
35fn is_msm_supported(context: &NativeContext) -> PartialVMResult<bool> {
36 Ok(get_extension!(context, ObjectRuntime)?
37 .protocol_config
38 .enable_group_ops_native_function_msm())
39}
40
41fn is_uncompressed_g1_supported(context: &NativeContext) -> PartialVMResult<bool> {
42 Ok(get_extension!(context, ObjectRuntime)?
43 .protocol_config
44 .uncompressed_g1_group_elements())
45}
46
47fn v2_native_charge(context: &NativeContext, cost: InternalGas) -> PartialVMResult<InternalGas> {
48 Ok(
49 if get_extension!(context, ObjectRuntime)?
50 .protocol_config
51 .native_charging_v2()
52 {
53 context.gas_used()
54 } else {
55 cost
56 },
57 )
58}
59
60fn map_op_result(
61 context: &NativeContext,
62 cost: InternalGas,
63 result: FastCryptoResult<Vec<u8>>,
64) -> PartialVMResult<NativeResult> {
65 match result {
66 Ok(bytes) => Ok(NativeResult::ok(
67 v2_native_charge(context, cost)?,
68 smallvec![Value::vector_u8(bytes)],
69 )),
70 Err(_) => Ok(NativeResult::err(
72 v2_native_charge(context, cost)?,
73 INVALID_INPUT_ERROR,
74 )),
75 }
76}
77
78#[derive(Clone)]
81pub struct GroupOpsCostParams {
82 pub bls12381_decode_scalar_cost: Option<InternalGas>,
84 pub bls12381_decode_g1_cost: Option<InternalGas>,
85 pub bls12381_decode_g2_cost: Option<InternalGas>,
86 pub bls12381_decode_gt_cost: Option<InternalGas>,
87 pub bls12381_scalar_add_cost: Option<InternalGas>,
89 pub bls12381_g1_add_cost: Option<InternalGas>,
90 pub bls12381_g2_add_cost: Option<InternalGas>,
91 pub bls12381_gt_add_cost: Option<InternalGas>,
92 pub bls12381_scalar_sub_cost: Option<InternalGas>,
94 pub bls12381_g1_sub_cost: Option<InternalGas>,
95 pub bls12381_g2_sub_cost: Option<InternalGas>,
96 pub bls12381_gt_sub_cost: Option<InternalGas>,
97 pub bls12381_scalar_mul_cost: Option<InternalGas>,
99 pub bls12381_g1_mul_cost: Option<InternalGas>,
100 pub bls12381_g2_mul_cost: Option<InternalGas>,
101 pub bls12381_gt_mul_cost: Option<InternalGas>,
102 pub bls12381_scalar_div_cost: Option<InternalGas>,
104 pub bls12381_g1_div_cost: Option<InternalGas>,
105 pub bls12381_g2_div_cost: Option<InternalGas>,
106 pub bls12381_gt_div_cost: Option<InternalGas>,
107 pub bls12381_g1_hash_to_base_cost: Option<InternalGas>,
109 pub bls12381_g2_hash_to_base_cost: Option<InternalGas>,
110 pub bls12381_g1_hash_to_cost_per_byte: Option<InternalGas>,
111 pub bls12381_g2_hash_to_cost_per_byte: Option<InternalGas>,
112 pub bls12381_g1_msm_base_cost: Option<InternalGas>,
114 pub bls12381_g2_msm_base_cost: Option<InternalGas>,
115 pub bls12381_g1_msm_base_cost_per_input: Option<InternalGas>,
117 pub bls12381_g2_msm_base_cost_per_input: Option<InternalGas>,
118 pub bls12381_msm_max_len: Option<u32>,
120 pub bls12381_pairing_cost: Option<InternalGas>,
122 pub bls12381_g1_to_uncompressed_g1_cost: Option<InternalGas>,
124 pub bls12381_uncompressed_g1_to_g1_cost: Option<InternalGas>,
125 pub bls12381_uncompressed_g1_sum_base_cost: Option<InternalGas>,
127 pub bls12381_uncompressed_g1_sum_cost_per_term: Option<InternalGas>,
128 pub bls12381_uncompressed_g1_sum_max_terms: Option<u64>,
130}
131
132macro_rules! native_charge_gas_early_exit_option {
133 ($native_context:ident, $cost:expr) => {{
134 use move_binary_format::errors::PartialVMError;
135 use move_core_types::vm_status::StatusCode;
136 native_charge_gas_early_exit!(
137 $native_context,
138 $cost.ok_or_else(|| {
139 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
140 .with_message("Gas cost for group ops is missing".to_string())
141 })?
142 );
143 }};
144}
145
146#[repr(u8)]
148enum Groups {
149 BLS12381Scalar = 0,
150 BLS12381G1 = 1,
151 BLS12381G2 = 2,
152 BLS12381GT = 3,
153 BLS12381UncompressedG1 = 4,
154}
155
156impl Groups {
157 fn from_u8(value: u8) -> Option<Self> {
158 match value {
159 0 => Some(Groups::BLS12381Scalar),
160 1 => Some(Groups::BLS12381G1),
161 2 => Some(Groups::BLS12381G2),
162 3 => Some(Groups::BLS12381GT),
163 4 => Some(Groups::BLS12381UncompressedG1),
164 _ => None,
165 }
166 }
167}
168
169fn parse_untrusted<G: ToFromByteArray<S> + FromTrustedByteArray<S>, const S: usize>(
170 e: &[u8],
171) -> FastCryptoResult<G> {
172 G::from_byte_array(e.try_into().map_err(|_| FastCryptoError::InvalidInput)?)
173}
174
175fn parse_trusted<G: ToFromByteArray<S> + FromTrustedByteArray<S>, const S: usize>(
176 e: &[u8],
177) -> FastCryptoResult<G> {
178 G::from_trusted_byte_array(e.try_into().map_err(|_| FastCryptoError::InvalidInput)?)
179}
180
181fn binary_op_diff<
183 G1: ToFromByteArray<S1> + FromTrustedByteArray<S1>,
184 G2: ToFromByteArray<S2> + FromTrustedByteArray<S2>,
185 const S1: usize,
186 const S2: usize,
187>(
188 op: impl Fn(G1, G2) -> FastCryptoResult<G2>,
189 a1: &[u8],
190 a2: &[u8],
191) -> FastCryptoResult<Vec<u8>> {
192 let e1 = parse_trusted::<G1, S1>(a1)?;
193 let e2 = parse_trusted::<G2, S2>(a2)?;
194 let result = op(e1, e2)?;
195 Ok(result.to_byte_array().to_vec())
196}
197
198fn binary_op<G: ToFromByteArray<S> + FromTrustedByteArray<S>, const S: usize>(
200 op: impl Fn(G, G) -> FastCryptoResult<G>,
201 a1: &[u8],
202 a2: &[u8],
203) -> FastCryptoResult<Vec<u8>> {
204 binary_op_diff::<G, G, S, S>(op, a1, a2)
205}
206
207pub fn internal_validate(
218 context: &mut NativeContext,
219 ty_args: Vec<Type>,
220 mut args: VecDeque<Value>,
221) -> PartialVMResult<NativeResult> {
222 debug_assert!(ty_args.is_empty());
223 debug_assert!(args.len() == 2);
224
225 let cost = context.gas_used();
226 if !is_supported(context)? {
227 return Ok(NativeResult::err(cost, NOT_SUPPORTED_ERROR));
228 }
229
230 let bytes_ref = pop_arg!(args, VectorRef);
231 let bytes = bytes_ref.as_bytes_ref();
232 let group_type = pop_arg!(args, u8);
233
234 let cost_params = get_extension!(context, NativesCostTable)?
235 .group_ops_cost_params
236 .clone();
237
238 let result = match Groups::from_u8(group_type) {
239 Some(Groups::BLS12381Scalar) => {
240 native_charge_gas_early_exit_option!(context, cost_params.bls12381_decode_scalar_cost);
241 parse_untrusted::<bls::Scalar, { bls::Scalar::BYTE_LENGTH }>(&bytes).is_ok()
242 }
243 Some(Groups::BLS12381G1) => {
244 native_charge_gas_early_exit_option!(context, cost_params.bls12381_decode_g1_cost);
245 parse_untrusted::<bls::G1Element, { bls::G1Element::BYTE_LENGTH }>(&bytes).is_ok()
246 }
247 Some(Groups::BLS12381G2) => {
248 native_charge_gas_early_exit_option!(context, cost_params.bls12381_decode_g2_cost);
249 parse_untrusted::<bls::G2Element, { bls::G2Element::BYTE_LENGTH }>(&bytes).is_ok()
250 }
251 _ => false,
252 };
253
254 Ok(NativeResult::ok(
255 v2_native_charge(context, cost)?,
256 smallvec![Value::bool(result)],
257 ))
258}
259
260pub fn internal_add(
266 context: &mut NativeContext,
267 ty_args: Vec<Type>,
268 mut args: VecDeque<Value>,
269) -> PartialVMResult<NativeResult> {
270 debug_assert!(ty_args.is_empty());
271 debug_assert!(args.len() == 3);
272
273 let cost = context.gas_used();
274 if !is_supported(context)? {
275 return Ok(NativeResult::err(cost, NOT_SUPPORTED_ERROR));
276 }
277
278 let e2_ref = pop_arg!(args, VectorRef);
279 let e2 = e2_ref.as_bytes_ref();
280 let e1_ref = pop_arg!(args, VectorRef);
281 let e1 = e1_ref.as_bytes_ref();
282 let group_type = pop_arg!(args, u8);
283
284 let cost_params = get_extension!(context, NativesCostTable)?
285 .group_ops_cost_params
286 .clone();
287
288 let result = match Groups::from_u8(group_type) {
289 Some(Groups::BLS12381Scalar) => {
290 native_charge_gas_early_exit_option!(context, cost_params.bls12381_scalar_add_cost);
291 binary_op::<bls::Scalar, { bls::Scalar::BYTE_LENGTH }>(|a, b| Ok(a + b), &e1, &e2)
292 }
293 Some(Groups::BLS12381G1) => {
294 native_charge_gas_early_exit_option!(context, cost_params.bls12381_g1_add_cost);
295 binary_op::<bls::G1Element, { bls::G1Element::BYTE_LENGTH }>(|a, b| Ok(a + b), &e1, &e2)
296 }
297 Some(Groups::BLS12381G2) => {
298 native_charge_gas_early_exit_option!(context, cost_params.bls12381_g2_add_cost);
299 binary_op::<bls::G2Element, { bls::G2Element::BYTE_LENGTH }>(|a, b| Ok(a + b), &e1, &e2)
300 }
301 Some(Groups::BLS12381GT) => {
302 native_charge_gas_early_exit_option!(context, cost_params.bls12381_gt_add_cost);
303 binary_op::<bls::GTElement, { bls::GTElement::BYTE_LENGTH }>(|a, b| Ok(a + b), &e1, &e2)
304 }
305 _ => Err(FastCryptoError::InvalidInput),
306 };
307
308 map_op_result(context, cost, result)
309}
310
311pub fn internal_sub(
317 context: &mut NativeContext,
318 ty_args: Vec<Type>,
319 mut args: VecDeque<Value>,
320) -> PartialVMResult<NativeResult> {
321 debug_assert!(ty_args.is_empty());
322 debug_assert!(args.len() == 3);
323
324 let cost = context.gas_used();
325 if !is_supported(context)? {
326 return Ok(NativeResult::err(cost, NOT_SUPPORTED_ERROR));
327 }
328
329 let e2_ref = pop_arg!(args, VectorRef);
330 let e2 = e2_ref.as_bytes_ref();
331 let e1_ref = pop_arg!(args, VectorRef);
332 let e1 = e1_ref.as_bytes_ref();
333 let group_type = pop_arg!(args, u8);
334
335 let cost_params = get_extension!(context, NativesCostTable)?
336 .group_ops_cost_params
337 .clone();
338
339 let result = match Groups::from_u8(group_type) {
340 Some(Groups::BLS12381Scalar) => {
341 native_charge_gas_early_exit_option!(context, cost_params.bls12381_scalar_sub_cost);
342 binary_op::<bls::Scalar, { bls::Scalar::BYTE_LENGTH }>(|a, b| Ok(a - b), &e1, &e2)
343 }
344 Some(Groups::BLS12381G1) => {
345 native_charge_gas_early_exit_option!(context, cost_params.bls12381_g1_sub_cost);
346 binary_op::<bls::G1Element, { bls::G1Element::BYTE_LENGTH }>(|a, b| Ok(a - b), &e1, &e2)
347 }
348 Some(Groups::BLS12381G2) => {
349 native_charge_gas_early_exit_option!(context, cost_params.bls12381_g2_sub_cost);
350 binary_op::<bls::G2Element, { bls::G2Element::BYTE_LENGTH }>(|a, b| Ok(a - b), &e1, &e2)
351 }
352 Some(Groups::BLS12381GT) => {
353 native_charge_gas_early_exit_option!(context, cost_params.bls12381_gt_sub_cost);
354 binary_op::<bls::GTElement, { bls::GTElement::BYTE_LENGTH }>(|a, b| Ok(a - b), &e1, &e2)
355 }
356 _ => Err(FastCryptoError::InvalidInput),
357 };
358
359 map_op_result(context, cost, result)
360}
361
362pub fn internal_mul(
368 context: &mut NativeContext,
369 ty_args: Vec<Type>,
370 mut args: VecDeque<Value>,
371) -> PartialVMResult<NativeResult> {
372 debug_assert!(ty_args.is_empty());
373 debug_assert!(args.len() == 3);
374
375 let cost = context.gas_used();
376 if !is_supported(context)? {
377 return Ok(NativeResult::err(cost, NOT_SUPPORTED_ERROR));
378 }
379
380 let e2_ref = pop_arg!(args, VectorRef);
381 let e2 = e2_ref.as_bytes_ref();
382 let e1_ref = pop_arg!(args, VectorRef);
383 let e1 = e1_ref.as_bytes_ref();
384 let group_type = pop_arg!(args, u8);
385
386 let cost_params = get_extension!(context, NativesCostTable)?
387 .group_ops_cost_params
388 .clone();
389
390 let result = match Groups::from_u8(group_type) {
391 Some(Groups::BLS12381Scalar) => {
392 native_charge_gas_early_exit_option!(context, cost_params.bls12381_scalar_mul_cost);
393 binary_op::<bls::Scalar, { bls::Scalar::BYTE_LENGTH }>(|a, b| Ok(b * a), &e1, &e2)
394 }
395 Some(Groups::BLS12381G1) => {
396 native_charge_gas_early_exit_option!(context, cost_params.bls12381_g1_mul_cost);
397 binary_op_diff::<
398 bls::Scalar,
399 bls::G1Element,
400 { bls::Scalar::BYTE_LENGTH },
401 { bls::G1Element::BYTE_LENGTH },
402 >(|a, b| Ok(b * a), &e1, &e2)
403 }
404 Some(Groups::BLS12381G2) => {
405 native_charge_gas_early_exit_option!(context, cost_params.bls12381_g2_mul_cost);
406 binary_op_diff::<
407 bls::Scalar,
408 bls::G2Element,
409 { bls::Scalar::BYTE_LENGTH },
410 { bls::G2Element::BYTE_LENGTH },
411 >(|a, b| Ok(b * a), &e1, &e2)
412 }
413 Some(Groups::BLS12381GT) => {
414 native_charge_gas_early_exit_option!(context, cost_params.bls12381_gt_mul_cost);
415 binary_op_diff::<
416 bls::Scalar,
417 bls::GTElement,
418 { bls::Scalar::BYTE_LENGTH },
419 { bls::GTElement::BYTE_LENGTH },
420 >(|a, b| Ok(b * a), &e1, &e2)
421 }
422 _ => Err(FastCryptoError::InvalidInput),
423 };
424
425 map_op_result(context, cost, result)
426}
427
428pub fn internal_div(
434 context: &mut NativeContext,
435 ty_args: Vec<Type>,
436 mut args: VecDeque<Value>,
437) -> PartialVMResult<NativeResult> {
438 debug_assert!(ty_args.is_empty());
439 debug_assert!(args.len() == 3);
440
441 let cost = context.gas_used();
442 if !is_supported(context)? {
443 return Ok(NativeResult::err(cost, NOT_SUPPORTED_ERROR));
444 }
445
446 let e2_ref = pop_arg!(args, VectorRef);
447 let e2 = e2_ref.as_bytes_ref();
448 let e1_ref = pop_arg!(args, VectorRef);
449 let e1 = e1_ref.as_bytes_ref();
450 let group_type = pop_arg!(args, u8);
451
452 let cost_params = get_extension!(context, NativesCostTable)?
453 .group_ops_cost_params
454 .clone();
455
456 let result = match Groups::from_u8(group_type) {
457 Some(Groups::BLS12381Scalar) => {
458 native_charge_gas_early_exit_option!(context, cost_params.bls12381_scalar_div_cost);
459 binary_op::<bls::Scalar, { bls::Scalar::BYTE_LENGTH }>(|a, b| b / a, &e1, &e2)
460 }
461 Some(Groups::BLS12381G1) => {
462 native_charge_gas_early_exit_option!(context, cost_params.bls12381_g1_div_cost);
463 binary_op_diff::<
464 bls::Scalar,
465 bls::G1Element,
466 { bls::Scalar::BYTE_LENGTH },
467 { bls::G1Element::BYTE_LENGTH },
468 >(|a, b| b / a, &e1, &e2)
469 }
470 Some(Groups::BLS12381G2) => {
471 native_charge_gas_early_exit_option!(context, cost_params.bls12381_g2_div_cost);
472 binary_op_diff::<
473 bls::Scalar,
474 bls::G2Element,
475 { bls::Scalar::BYTE_LENGTH },
476 { bls::G2Element::BYTE_LENGTH },
477 >(|a, b| b / a, &e1, &e2)
478 }
479 Some(Groups::BLS12381GT) => {
480 native_charge_gas_early_exit_option!(context, cost_params.bls12381_gt_div_cost);
481 binary_op_diff::<
482 bls::Scalar,
483 bls::GTElement,
484 { bls::Scalar::BYTE_LENGTH },
485 { bls::GTElement::BYTE_LENGTH },
486 >(|a, b| b / a, &e1, &e2)
487 }
488 _ => Err(FastCryptoError::InvalidInput),
489 };
490
491 map_op_result(context, cost, result)
492}
493
494pub fn internal_hash_to(
501 context: &mut NativeContext,
502 ty_args: Vec<Type>,
503 mut args: VecDeque<Value>,
504) -> PartialVMResult<NativeResult> {
505 debug_assert!(ty_args.is_empty());
506 debug_assert!(args.len() == 2);
507
508 let cost = context.gas_used();
509 if !is_supported(context)? {
510 return Ok(NativeResult::err(cost, NOT_SUPPORTED_ERROR));
511 }
512
513 let m_ref = pop_arg!(args, VectorRef);
514 let m = m_ref.as_bytes_ref();
515 let group_type = pop_arg!(args, u8);
516
517 if m.is_empty() {
518 return Ok(NativeResult::err(cost, INVALID_INPUT_ERROR));
519 }
520
521 let cost_params = get_extension!(context, NativesCostTable)?
522 .group_ops_cost_params
523 .clone();
524
525 let result = match Groups::from_u8(group_type) {
526 Some(Groups::BLS12381G1) => {
527 native_charge_gas_early_exit_option!(
528 context,
529 cost_params
530 .bls12381_g1_hash_to_base_cost
531 .and_then(|base_cost| cost_params
532 .bls12381_g1_hash_to_cost_per_byte
533 .map(|per_byte| base_cost + per_byte * (m.len() as u64).into()))
534 );
535 Ok(bls::G1Element::hash_to_group_element(&m)
536 .to_byte_array()
537 .to_vec())
538 }
539 Some(Groups::BLS12381G2) => {
540 native_charge_gas_early_exit_option!(
541 context,
542 cost_params
543 .bls12381_g2_hash_to_base_cost
544 .and_then(|base_cost| cost_params
545 .bls12381_g2_hash_to_cost_per_byte
546 .map(|per_byte| base_cost + per_byte * (m.len() as u64).into()))
547 );
548 Ok(bls::G2Element::hash_to_group_element(&m)
549 .to_byte_array()
550 .to_vec())
551 }
552 _ => Err(FastCryptoError::InvalidInput),
553 };
554
555 map_op_result(context, cost, result)
556}
557
558fn msm_num_of_additions(n: u64) -> u64 {
560 debug_assert!(n > 0);
561 let wbits = (64 - n.leading_zeros() - 1) as u64;
562 let window_size = match wbits {
563 0 => 1,
564 1..=4 => 2,
565 5..=12 => wbits - 2,
566 _ => wbits - 3,
567 };
568 let num_of_windows = 255 / window_size + if 255 % window_size == 0 { 0 } else { 1 };
569 num_of_windows * (n + (1 << window_size) + 1)
570}
571
572#[test]
573fn test_msm_factor() {
574 assert_eq!(msm_num_of_additions(1), 1020);
575 assert_eq!(msm_num_of_additions(2), 896);
576 assert_eq!(msm_num_of_additions(3), 1024);
577 assert_eq!(msm_num_of_additions(4), 1152);
578 assert_eq!(msm_num_of_additions(32), 3485);
579}
580
581fn multi_scalar_mul<G, const SCALAR_SIZE: usize, const POINT_SIZE: usize>(
582 context: &mut NativeContext,
583 scalar_decode_cost: Option<InternalGas>,
584 point_decode_cost: Option<InternalGas>,
585 base_cost: Option<InternalGas>,
586 base_cost_per_addition: Option<InternalGas>,
587 max_len: u32,
588 scalars: &[u8],
589 points: &[u8],
590) -> PartialVMResult<NativeResult>
591where
592 G: GroupElement
593 + ToFromByteArray<POINT_SIZE>
594 + FromTrustedByteArray<POINT_SIZE>
595 + MultiScalarMul,
596 G::ScalarType: ToFromByteArray<SCALAR_SIZE> + FromTrustedByteArray<SCALAR_SIZE>,
597{
598 if points.is_empty()
599 || scalars.is_empty()
600 || !scalars.len().is_multiple_of(SCALAR_SIZE)
601 || !points.len().is_multiple_of(POINT_SIZE)
602 || points.len() / POINT_SIZE != scalars.len() / SCALAR_SIZE
603 {
604 return Ok(NativeResult::err(context.gas_used(), INVALID_INPUT_ERROR));
605 }
606
607 if points.len() / POINT_SIZE > max_len as usize {
608 return Ok(NativeResult::err(context.gas_used(), INPUT_TOO_LONG_ERROR));
609 }
610
611 native_charge_gas_early_exit_option!(
612 context,
613 scalar_decode_cost.map(|cost| cost * ((scalars.len() / SCALAR_SIZE) as u64).into())
614 );
615 let scalars = scalars
616 .chunks(SCALAR_SIZE)
617 .map(parse_trusted::<G::ScalarType, { SCALAR_SIZE }>)
618 .collect::<Result<Vec<_>, _>>();
619
620 native_charge_gas_early_exit_option!(
621 context,
622 point_decode_cost.map(|cost| cost * ((points.len() / POINT_SIZE) as u64).into())
623 );
624 let points = points
625 .chunks(POINT_SIZE)
626 .map(parse_trusted::<G, { POINT_SIZE }>)
627 .collect::<Result<Vec<_>, _>>();
628
629 if let (Ok(scalars), Ok(points)) = (scalars, points) {
630 let num_of_additions = msm_num_of_additions(scalars.len() as u64);
632 native_charge_gas_early_exit_option!(
633 context,
634 base_cost.and_then(|base| base_cost_per_addition
635 .map(|per_addition| base + per_addition * num_of_additions.into()))
636 );
637
638 let r = G::multi_scalar_mul(&scalars, &points)
639 .expect("Already checked the lengths of the vectors");
640 Ok(NativeResult::ok(
641 context.gas_used(),
642 smallvec![Value::vector_u8(r.to_byte_array().to_vec())],
643 ))
644 } else {
645 Ok(NativeResult::err(context.gas_used(), INVALID_INPUT_ERROR))
646 }
647}
648
649pub fn internal_multi_scalar_mul(
656 context: &mut NativeContext,
657 ty_args: Vec<Type>,
658 mut args: VecDeque<Value>,
659) -> PartialVMResult<NativeResult> {
660 debug_assert!(ty_args.is_empty());
661 debug_assert!(args.len() == 3);
662
663 let cost = context.gas_used();
664 if !is_msm_supported(context)? {
665 return Ok(NativeResult::err(cost, NOT_SUPPORTED_ERROR));
666 }
667
668 let elements_ref = pop_arg!(args, VectorRef);
669 let elements = elements_ref.as_bytes_ref();
670 let scalars_ref = pop_arg!(args, VectorRef);
671 let scalars = scalars_ref.as_bytes_ref();
672 let group_type = pop_arg!(args, u8);
673
674 let cost_params = get_extension!(context, NativesCostTable)?
675 .group_ops_cost_params
676 .clone();
677
678 let max_len = cost_params.bls12381_msm_max_len.ok_or_else(|| {
679 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
680 .with_message("Max len for MSM is not set".to_string())
681 })?;
682
683 match Groups::from_u8(group_type) {
685 Some(Groups::BLS12381G1) => multi_scalar_mul::<
686 bls::G1Element,
687 { bls::Scalar::BYTE_LENGTH },
688 { bls::G1Element::BYTE_LENGTH },
689 >(
690 context,
691 cost_params.bls12381_decode_scalar_cost,
692 cost_params.bls12381_decode_g1_cost,
693 cost_params.bls12381_g1_msm_base_cost,
694 cost_params.bls12381_g1_msm_base_cost_per_input,
695 max_len,
696 scalars.as_ref(),
697 elements.as_ref(),
698 ),
699 Some(Groups::BLS12381G2) => multi_scalar_mul::<
700 bls::G2Element,
701 { bls::Scalar::BYTE_LENGTH },
702 { bls::G2Element::BYTE_LENGTH },
703 >(
704 context,
705 cost_params.bls12381_decode_scalar_cost,
706 cost_params.bls12381_decode_g2_cost,
707 cost_params.bls12381_g2_msm_base_cost,
708 cost_params.bls12381_g2_msm_base_cost_per_input,
709 max_len,
710 scalars.as_ref(),
711 elements.as_ref(),
712 ),
713 _ => Ok(NativeResult::err(
714 v2_native_charge(context, cost)?,
715 INVALID_INPUT_ERROR,
716 )),
717 }
718}
719
720pub fn internal_pairing(
726 context: &mut NativeContext,
727 ty_args: Vec<Type>,
728 mut args: VecDeque<Value>,
729) -> PartialVMResult<NativeResult> {
730 debug_assert!(ty_args.is_empty());
731 debug_assert!(args.len() == 3);
732
733 let cost = context.gas_used();
734 if !is_supported(context)? {
735 return Ok(NativeResult::err(cost, NOT_SUPPORTED_ERROR));
736 }
737
738 let e2_ref = pop_arg!(args, VectorRef);
739 let e2 = e2_ref.as_bytes_ref();
740 let e1_ref = pop_arg!(args, VectorRef);
741 let e1 = e1_ref.as_bytes_ref();
742 let group_type = pop_arg!(args, u8);
743
744 let cost_params = get_extension!(context, NativesCostTable)?
745 .group_ops_cost_params
746 .clone();
747
748 let result = match Groups::from_u8(group_type) {
749 Some(Groups::BLS12381G1) => {
750 native_charge_gas_early_exit_option!(context, cost_params.bls12381_pairing_cost);
751 parse_trusted::<bls::G1Element, { bls::G1Element::BYTE_LENGTH }>(&e1).and_then(|e1| {
752 parse_trusted::<bls::G2Element, { bls::G2Element::BYTE_LENGTH }>(&e2).map(|e2| {
753 let e3 = e1.pairing(&e2);
754 e3.to_byte_array().to_vec()
755 })
756 })
757 }
758 _ => Err(FastCryptoError::InvalidInput),
759 };
760
761 map_op_result(context, cost, result)
762}
763
764pub fn internal_convert(
770 context: &mut NativeContext,
771 ty_args: Vec<Type>,
772 mut args: VecDeque<Value>,
773) -> PartialVMResult<NativeResult> {
774 debug_assert!(ty_args.is_empty());
775 debug_assert!(args.len() == 3);
776
777 let cost = context.gas_used();
778
779 if !(is_uncompressed_g1_supported(context))? {
780 return Ok(NativeResult::err(cost, NOT_SUPPORTED_ERROR));
781 }
782
783 let e_ref = pop_arg!(args, VectorRef);
784 let e = e_ref.as_bytes_ref();
785 let to_type = pop_arg!(args, u8);
786 let from_type = pop_arg!(args, u8);
787
788 let cost_params = get_extension!(context, NativesCostTable)?
789 .group_ops_cost_params
790 .clone();
791
792 let result = match (Groups::from_u8(from_type), Groups::from_u8(to_type)) {
793 (Some(Groups::BLS12381UncompressedG1), Some(Groups::BLS12381G1)) => {
794 native_charge_gas_early_exit_option!(
795 context,
796 cost_params.bls12381_uncompressed_g1_to_g1_cost
797 );
798 e.to_vec()
799 .try_into()
800 .map_err(|_| FastCryptoError::InvalidInput)
801 .map(bls::G1ElementUncompressed::from_trusted_byte_array)
802 .and_then(|e| bls::G1Element::try_from(&e))
803 .map(|e| e.to_byte_array().to_vec())
804 }
805 (Some(Groups::BLS12381G1), Some(Groups::BLS12381UncompressedG1)) => {
806 native_charge_gas_early_exit_option!(
807 context,
808 cost_params.bls12381_g1_to_uncompressed_g1_cost
809 );
810 parse_trusted::<bls::G1Element, { bls::G1Element::BYTE_LENGTH }>(&e)
811 .map(|e| bls::G1ElementUncompressed::from(&e))
812 .map(|e| e.into_byte_array().to_vec())
813 }
814 _ => Err(FastCryptoError::InvalidInput),
815 };
816
817 map_op_result(context, cost, result)
818}
819
820pub fn internal_sum(
826 context: &mut NativeContext,
827 ty_args: Vec<Type>,
828 mut args: VecDeque<Value>,
829) -> PartialVMResult<NativeResult> {
830 debug_assert!(ty_args.is_empty());
831 debug_assert!(args.len() == 2);
832
833 let cost = context.gas_used();
834
835 if !(is_uncompressed_g1_supported(context))? {
836 return Ok(NativeResult::err(cost, NOT_SUPPORTED_ERROR));
837 }
838
839 let cost_params = get_extension!(context, NativesCostTable)?
840 .group_ops_cost_params
841 .clone();
842
843 let inputs = pop_arg!(args, VectorRef);
845 let group_type = pop_arg!(args, u8);
846
847 let length = inputs
848 .len(&Type::Vector(Box::new(Type::U8)))?
849 .value_as::<u64>()?;
850
851 let result = match Groups::from_u8(group_type) {
852 Some(Groups::BLS12381UncompressedG1) => {
853 let max_terms = cost_params
854 .bls12381_uncompressed_g1_sum_max_terms
855 .ok_or_else(|| {
856 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
857 .with_message("Max number of terms is not set".to_string())
858 })?;
859
860 if length > max_terms {
861 return Ok(NativeResult::err(cost, INPUT_TOO_LONG_ERROR));
862 }
863
864 native_charge_gas_early_exit_option!(
865 context,
866 cost_params
867 .bls12381_uncompressed_g1_sum_base_cost
868 .and_then(|base| cost_params
869 .bls12381_uncompressed_g1_sum_cost_per_term
870 .map(|per_term| base + per_term * length.into()))
871 );
872
873 (0..length)
875 .map(|i| {
876 inputs
877 .borrow_elem(i as usize, &Type::Vector(Box::new(Type::U8)))
878 .and_then(Value::value_as::<VectorRef>)
879 .map_err(|_| FastCryptoError::InvalidInput)
880 .and_then(|v| {
881 v.as_bytes_ref()
882 .to_vec()
883 .try_into()
884 .map_err(|_| FastCryptoError::InvalidInput)
885 })
886 .map(bls::G1ElementUncompressed::from_trusted_byte_array)
887 })
888 .collect::<FastCryptoResult<Vec<_>>>()
889 .and_then(|e| bls::G1ElementUncompressed::sum(&e))
890 .map(|e| bls::G1ElementUncompressed::from(&e))
891 .map(|e| e.into_byte_array().to_vec())
892 }
893 _ => Err(FastCryptoError::InvalidInput),
894 };
895
896 map_op_result(context, cost, result)
897}