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