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