From 0c7123ab0a70bdf1be3c72ea868f35127acb7429 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Wed, 26 Mar 2025 09:49:15 +0000 Subject: [PATCH 01/13] move methods that use blst types into wrapper --- crates/precompile/src/bls12_381/blst.rs | 296 +++++++++++++++++++++++- 1 file changed, 292 insertions(+), 4 deletions(-) diff --git a/crates/precompile/src/bls12_381/blst.rs b/crates/precompile/src/bls12_381/blst.rs index 4b02f92b27..692e7b6f25 100644 --- a/crates/precompile/src/bls12_381/blst.rs +++ b/crates/precompile/src/bls12_381/blst.rs @@ -1,11 +1,19 @@ // This module contains a safe wrapper around the blst library. -use crate::bls12_381_const::SCALAR_LENGTH; +use crate::{ + bls12_381::utils::{is_valid_be, remove_padding}, + bls12_381_const::{ + FP_LENGTH, FP_PAD_BY, PADDED_FP_LENGTH, PADDED_G1_LENGTH, PADDED_G2_LENGTH, SCALAR_LENGTH, + }, + PrecompileError, +}; use blst::{ - blst_final_exp, blst_fp, blst_fp12, blst_fp12_is_one, blst_fp12_mul, blst_fp2, blst_map_to_g1, - blst_map_to_g2, blst_miller_loop, blst_p1, blst_p1_add_or_double_affine, blst_p1_affine, + blst_bendian_from_fp, blst_final_exp, blst_fp, blst_fp12, blst_fp12_is_one, blst_fp12_mul, + blst_fp2, blst_fp_from_bendian, blst_map_to_g1, blst_map_to_g2, blst_miller_loop, blst_p1, + blst_p1_add_or_double_affine, blst_p1_affine, blst_p1_affine_in_g1, blst_p1_affine_on_curve, blst_p1_from_affine, blst_p1_mult, blst_p1_to_affine, blst_p2, blst_p2_add_or_double_affine, - blst_p2_affine, blst_p2_from_affine, blst_p2_mult, blst_p2_to_affine, MultiPoint, + blst_p2_affine, blst_p2_affine_in_g2, blst_p2_affine_on_curve, blst_p2_from_affine, + blst_p2_mult, blst_p2_to_affine, blst_scalar, blst_scalar_from_bendian, MultiPoint, }; #[inline] @@ -314,3 +322,283 @@ pub(super) fn pairing_check(pairs: &[(blst_p1_affine, blst_p2_affine)]) -> bool // Check if the result is one (identity element) is_fp12_one(&final_result) } + +/// Encodes a G1 point in affine format into byte slice with padded elements. +pub(super) fn encode_g1_point(input: *const blst_p1_affine) -> [u8; PADDED_G1_LENGTH] { + let mut out = [0u8; PADDED_G1_LENGTH]; + // SAFETY: Out comes from fixed length array, input is a blst value. + unsafe { + fp_to_bytes(&mut out[..PADDED_FP_LENGTH], &(*input).x); + fp_to_bytes(&mut out[PADDED_FP_LENGTH..], &(*input).y); + } + out +} + +/// Encodes a single finite field element into byte slice with padding. +fn fp_to_bytes(out: &mut [u8], input: *const blst_fp) { + if out.len() != PADDED_FP_LENGTH { + return; + } + let (padding, rest) = out.split_at_mut(FP_PAD_BY); + padding.fill(0); + // SAFETY: Out length is checked previously, `input` is a blst value. + unsafe { blst_bendian_from_fp(rest.as_mut_ptr(), input) }; +} + +/// Returns a `blst_p1_affine` from the provided byte slices, which represent the x and y +/// affine coordinates of the point. +/// +/// - If the x or y coordinate do not represent a canonical field element, an error is returned. +/// See [fp_from_bendian] for more information. +/// - If the point is not on the curve, an error is returned. +fn decode_and_check_g1( + p0_x: &[u8; 48], + p0_y: &[u8; 48], +) -> Result { + let out = blst_p1_affine { + x: fp_from_bendian(p0_x)?, + y: fp_from_bendian(p0_y)?, + }; + + // From EIP-2537: + // + // Error cases: + // + // * An input is neither a point on the G1 elliptic curve nor the infinity point + // + // SAFETY: Out is a blst value. + if unsafe { !blst_p1_affine_on_curve(&out) } { + return Err(PrecompileError::Other( + "Element not on G1 curve".to_string(), + )); + } + + Ok(out) +} + +/// Extracts a G1 point in Affine format from a 128 byte slice representation. +/// +/// Note: By default, subgroup checks are performed. +pub(super) fn extract_g1_input(input: &[u8]) -> Result { + _extract_g1_input(input, true) +} +/// Extracts a G1 point in Affine format from a 128 byte slice representation. +/// without performing a subgroup check. +/// +/// Note: Skipping subgroup checks can introduce security issues. +/// This method should only be called if: +/// - The EIP specifies that no subgroup check should be performed +/// - One can be certain that the point is in the correct subgroup. +pub(super) fn extract_g1_input_no_subgroup_check( + input: &[u8], +) -> Result { + _extract_g1_input(input, false) +} +/// Extracts a G1 point in Affine format from a 128 byte slice representation. +/// +/// **Note**: This function will perform a G1 subgroup check if `subgroup_check` is set to `true`. +fn _extract_g1_input( + input: &[u8], + subgroup_check: bool, +) -> Result { + if input.len() != PADDED_G1_LENGTH { + return Err(PrecompileError::Other(format!( + "Input should be {PADDED_G1_LENGTH} bytes, was {}", + input.len() + ))); + } + + let input_p0_x = remove_padding(&input[..PADDED_FP_LENGTH])?; + let input_p0_y = remove_padding(&input[PADDED_FP_LENGTH..PADDED_G1_LENGTH])?; + let out = decode_and_check_g1(input_p0_x, input_p0_y)?; + + if subgroup_check { + // NB: Subgroup checks + // + // Scalar multiplications, MSMs and pairings MUST perform a subgroup check. + // + // Implementations SHOULD use the optimized subgroup check method: + // + // https://eips.ethereum.org/assets/eip-2537/fast_subgroup_checks + // + // On any input that fail the subgroup check, the precompile MUST return an error. + // + // As endomorphism acceleration requires input on the correct subgroup, implementers MAY + // use endomorphism acceleration. + if unsafe { !blst_p1_affine_in_g1(&out) } { + return Err(PrecompileError::Other("Element not in G1".to_string())); + } + } + Ok(out) +} + +/// Encodes a G2 point in affine format into byte slice with padded elements. +pub(super) fn encode_g2_point(input: &blst_p2_affine) -> [u8; PADDED_G2_LENGTH] { + let mut out = [0u8; PADDED_G2_LENGTH]; + fp_to_bytes(&mut out[..PADDED_FP_LENGTH], &input.x.fp[0]); + fp_to_bytes( + &mut out[PADDED_FP_LENGTH..2 * PADDED_FP_LENGTH], + &input.x.fp[1], + ); + fp_to_bytes( + &mut out[2 * PADDED_FP_LENGTH..3 * PADDED_FP_LENGTH], + &input.y.fp[0], + ); + fp_to_bytes( + &mut out[3 * PADDED_FP_LENGTH..4 * PADDED_FP_LENGTH], + &input.y.fp[1], + ); + out +} + +/// Returns a `blst_p2_affine` from the provided byte slices, which represent the x and y +/// affine coordinates of the point. +/// +/// - If the x or y coordinate do not represent a canonical field element, an error is returned. +/// See [check_canonical_fp2] for more information. +/// - If the point is not on the curve, an error is returned. +fn decode_and_check_g2( + x1: &[u8; 48], + x2: &[u8; 48], + y1: &[u8; 48], + y2: &[u8; 48], +) -> Result { + let out = blst_p2_affine { + x: check_canonical_fp2(x1, x2)?, + y: check_canonical_fp2(y1, y2)?, + }; + + // From EIP-2537: + // + // Error cases: + // + // * An input is neither a point on the G2 elliptic curve nor the infinity point + // + // SAFETY: Out is a blst value. + if unsafe { !blst_p2_affine_on_curve(&out) } { + return Err(PrecompileError::Other( + "Element not on G2 curve".to_string(), + )); + } + + Ok(out) +} + +/// Checks whether or not the input represents a canonical fp2 field element, returning the field +/// element if successful. +pub(super) fn check_canonical_fp2( + input_1: &[u8; 48], + input_2: &[u8; 48], +) -> Result { + let fp_1 = fp_from_bendian(input_1)?; + let fp_2 = fp_from_bendian(input_2)?; + + let fp2 = blst_fp2 { fp: [fp_1, fp_2] }; + + Ok(fp2) +} +/// Extracts a G2 point in Affine format from a 256 byte slice representation. +/// +/// Note: By default, no subgroup checks are performed. +pub(super) fn extract_g2_input(input: &[u8]) -> Result { + _extract_g2_input(input, true) +} +/// Extracts a G2 point in Affine format from a 256 byte slice representation +/// without performing a subgroup check. +/// +/// Note: Skipping subgroup checks can introduce security issues. +/// This method should only be called if: +/// - The EIP specifies that no subgroup check should be performed +/// - One can be certain that the point is in the correct subgroup. +pub(super) fn extract_g2_input_no_subgroup_check( + input: &[u8], +) -> Result { + _extract_g2_input(input, false) +} +/// Extracts a G2 point in Affine format from a 256 byte slice representation. +/// +/// **Note**: This function will perform a G2 subgroup check if `subgroup_check` is set to `true`. +fn _extract_g2_input( + input: &[u8], + subgroup_check: bool, +) -> Result { + if input.len() != PADDED_G2_LENGTH { + return Err(PrecompileError::Other(format!( + "Input should be {PADDED_G2_LENGTH} bytes, was {}", + input.len() + ))); + } + + let mut input_fps = [&[0; FP_LENGTH]; 4]; + for i in 0..4 { + input_fps[i] = remove_padding(&input[i * PADDED_FP_LENGTH..(i + 1) * PADDED_FP_LENGTH])?; + } + + let out = decode_and_check_g2(input_fps[0], input_fps[1], input_fps[2], input_fps[3])?; + + if subgroup_check { + // NB: Subgroup checks + // + // Scalar multiplications, MSMs and pairings MUST perform a subgroup check. + // + // Implementations SHOULD use the optimized subgroup check method: + // + // https://eips.ethereum.org/assets/eip-2537/fast_subgroup_checks + // + // On any input that fail the subgroup check, the precompile MUST return an error. + // + // As endomorphism acceleration requires input on the correct subgroup, implementers MAY + // use endomorphism acceleration. + if unsafe { !blst_p2_affine_in_g2(&out) } { + return Err(PrecompileError::Other("Element not in G2".to_string())); + } + } + Ok(out) +} + +/// Checks whether or not the input represents a canonical field element, returning the field +/// element if successful. +pub(super) fn fp_from_bendian(input: &[u8; 48]) -> Result { + if !is_valid_be(input) { + return Err(PrecompileError::Other("non-canonical fp value".to_string())); + } + let mut fp = blst_fp::default(); + // SAFETY: `input` has fixed length, and `fp` is a blst value. + unsafe { + // This performs the check for canonical field elements + blst_fp_from_bendian(&mut fp, input.as_ptr()); + } + + Ok(fp) +} + +/// Extracts a scalar from a 32 byte slice representation, decoding the input as a big endian +/// unsigned integer. If the input is not exactly 32 bytes long, an error is returned. +/// +/// From [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537): +/// * A scalar for the multiplication operation is encoded as 32 bytes by performing BigEndian +/// encoding of the corresponding (unsigned) integer. +/// +/// We do not check that the scalar is a canonical Fr element, because the EIP specifies: +/// * The corresponding integer is not required to be less than or equal than main subgroup order +/// `q`. +pub(super) fn extract_scalar_input(input: &[u8]) -> Result { + if input.len() != SCALAR_LENGTH { + return Err(PrecompileError::Other(format!( + "Input should be {SCALAR_LENGTH} bytes, was {}", + input.len() + ))); + } + + let mut out = blst_scalar::default(); + // SAFETY: `input` length is checked previously, out is a blst value. + unsafe { + // Note: We do not use `blst_scalar_fr_check` here because, from EIP-2537: + // + // * The corresponding integer is not required to be less than or equal than main subgroup + // order `q`. + blst_scalar_from_bendian(&mut out, input.as_ptr()) + }; + + Ok(out) +} From d793bdc0bfa57a7037ebd55c32d4fd3ec714e633 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Wed, 26 Mar 2025 09:49:35 +0000 Subject: [PATCH 02/13] g1.rs and g2.rs are no longer needed --- crates/precompile/src/bls12_381.rs | 3 +- crates/precompile/src/bls12_381/g1.rs | 103 -------------------- crates/precompile/src/bls12_381/g2.rs | 129 -------------------------- 3 files changed, 1 insertion(+), 234 deletions(-) delete mode 100644 crates/precompile/src/bls12_381/g1.rs delete mode 100644 crates/precompile/src/bls12_381/g2.rs diff --git a/crates/precompile/src/bls12_381.rs b/crates/precompile/src/bls12_381.rs index 5b640b54fb..3a29f2a903 100644 --- a/crates/precompile/src/bls12_381.rs +++ b/crates/precompile/src/bls12_381.rs @@ -1,10 +1,9 @@ use crate::PrecompileWithAddress; mod blst; -mod g1; + pub mod g1_add; pub mod g1_msm; -mod g2; pub mod g2_add; pub mod g2_msm; pub mod map_fp2_to_g2; diff --git a/crates/precompile/src/bls12_381/g1.rs b/crates/precompile/src/bls12_381/g1.rs deleted file mode 100644 index 01084f6bb6..0000000000 --- a/crates/precompile/src/bls12_381/g1.rs +++ /dev/null @@ -1,103 +0,0 @@ -use super::utils::{fp_from_bendian, fp_to_bytes, remove_padding}; -use crate::bls12_381_const::{PADDED_FP_LENGTH, PADDED_G1_LENGTH}; -use crate::PrecompileError; -use blst::{blst_p1_affine, blst_p1_affine_in_g1, blst_p1_affine_on_curve}; -use primitives::Bytes; - -/// Encodes a G1 point in affine format into byte slice with padded elements. -pub(super) fn encode_g1_point(input: *const blst_p1_affine) -> Bytes { - let mut out = vec![0u8; PADDED_G1_LENGTH]; - // SAFETY: Out comes from fixed length array, input is a blst value. - unsafe { - fp_to_bytes(&mut out[..PADDED_FP_LENGTH], &(*input).x); - fp_to_bytes(&mut out[PADDED_FP_LENGTH..], &(*input).y); - } - out.into() -} - -/// Returns a `blst_p1_affine` from the provided byte slices, which represent the x and y -/// affine coordinates of the point. -/// -/// - If the x or y coordinate do not represent a canonical field element, an error is returned. -/// See [fp_from_bendian] for more information. -/// - If the point is not on the curve, an error is returned. -pub(super) fn decode_and_check_g1( - p0_x: &[u8; 48], - p0_y: &[u8; 48], -) -> Result { - let out = blst_p1_affine { - x: fp_from_bendian(p0_x)?, - y: fp_from_bendian(p0_y)?, - }; - - // From EIP-2537: - // - // Error cases: - // - // * An input is neither a point on the G1 elliptic curve nor the infinity point - // - // SAFETY: Out is a blst value. - if unsafe { !blst_p1_affine_on_curve(&out) } { - return Err(PrecompileError::Other( - "Element not on G1 curve".to_string(), - )); - } - - Ok(out) -} - -/// Extracts a G1 point in Affine format from a 128 byte slice representation. -/// -/// Note: By default, subgroup checks are performed. -pub(super) fn extract_g1_input(input: &[u8]) -> Result { - _extract_g1_input(input, true) -} -/// Extracts a G1 point in Affine format from a 128 byte slice representation. -/// without performing a subgroup check. -/// -/// Note: Skipping subgroup checks can introduce security issues. -/// This method should only be called if: -/// - The EIP specifies that no subgroup check should be performed -/// - One can be certain that the point is in the correct subgroup. -pub(super) fn extract_g1_input_no_subgroup_check( - input: &[u8], -) -> Result { - _extract_g1_input(input, false) -} -/// Extracts a G1 point in Affine format from a 128 byte slice representation. -/// -/// **Note**: This function will perform a G1 subgroup check if `subgroup_check` is set to `true`. -fn _extract_g1_input( - input: &[u8], - subgroup_check: bool, -) -> Result { - if input.len() != PADDED_G1_LENGTH { - return Err(PrecompileError::Other(format!( - "Input should be {PADDED_G1_LENGTH} bytes, was {}", - input.len() - ))); - } - - let input_p0_x = remove_padding(&input[..PADDED_FP_LENGTH])?; - let input_p0_y = remove_padding(&input[PADDED_FP_LENGTH..PADDED_G1_LENGTH])?; - let out = decode_and_check_g1(input_p0_x, input_p0_y)?; - - if subgroup_check { - // NB: Subgroup checks - // - // Scalar multiplications, MSMs and pairings MUST perform a subgroup check. - // - // Implementations SHOULD use the optimized subgroup check method: - // - // https://eips.ethereum.org/assets/eip-2537/fast_subgroup_checks - // - // On any input that fail the subgroup check, the precompile MUST return an error. - // - // As endomorphism acceleration requires input on the correct subgroup, implementers MAY - // use endomorphism acceleration. - if unsafe { !blst_p1_affine_in_g1(&out) } { - return Err(PrecompileError::Other("Element not in G1".to_string())); - } - } - Ok(out) -} diff --git a/crates/precompile/src/bls12_381/g2.rs b/crates/precompile/src/bls12_381/g2.rs deleted file mode 100644 index 89b1b3c71e..0000000000 --- a/crates/precompile/src/bls12_381/g2.rs +++ /dev/null @@ -1,129 +0,0 @@ -use super::utils::{fp_from_bendian, fp_to_bytes, remove_padding}; -use crate::bls12_381_const::{FP_LENGTH, PADDED_FP_LENGTH, PADDED_G2_LENGTH}; -use crate::PrecompileError; -use blst::{blst_fp2, blst_p2_affine, blst_p2_affine_in_g2, blst_p2_affine_on_curve}; -use primitives::Bytes; - -/// Encodes a G2 point in affine format into byte slice with padded elements. -pub(super) fn encode_g2_point(input: &blst_p2_affine) -> Bytes { - let mut out = vec![0u8; PADDED_G2_LENGTH]; - fp_to_bytes(&mut out[..PADDED_FP_LENGTH], &input.x.fp[0]); - fp_to_bytes( - &mut out[PADDED_FP_LENGTH..2 * PADDED_FP_LENGTH], - &input.x.fp[1], - ); - fp_to_bytes( - &mut out[2 * PADDED_FP_LENGTH..3 * PADDED_FP_LENGTH], - &input.y.fp[0], - ); - fp_to_bytes( - &mut out[3 * PADDED_FP_LENGTH..4 * PADDED_FP_LENGTH], - &input.y.fp[1], - ); - out.into() -} - -/// Returns a `blst_p2_affine` from the provided byte slices, which represent the x and y -/// affine coordinates of the point. -/// -/// - If the x or y coordinate do not represent a canonical field element, an error is returned. -/// See [check_canonical_fp2] for more information. -/// - If the point is not on the curve, an error is returned. -pub(super) fn decode_and_check_g2( - x1: &[u8; 48], - x2: &[u8; 48], - y1: &[u8; 48], - y2: &[u8; 48], -) -> Result { - let out = blst_p2_affine { - x: check_canonical_fp2(x1, x2)?, - y: check_canonical_fp2(y1, y2)?, - }; - - // From EIP-2537: - // - // Error cases: - // - // * An input is neither a point on the G2 elliptic curve nor the infinity point - // - // SAFETY: Out is a blst value. - if unsafe { !blst_p2_affine_on_curve(&out) } { - return Err(PrecompileError::Other( - "Element not on G2 curve".to_string(), - )); - } - - Ok(out) -} - -/// Checks whether or not the input represents a canonical fp2 field element, returning the field -/// element if successful. -pub(super) fn check_canonical_fp2( - input_1: &[u8; 48], - input_2: &[u8; 48], -) -> Result { - let fp_1 = fp_from_bendian(input_1)?; - let fp_2 = fp_from_bendian(input_2)?; - - let fp2 = blst_fp2 { fp: [fp_1, fp_2] }; - - Ok(fp2) -} -/// Extracts a G2 point in Affine format from a 256 byte slice representation. -/// -/// Note: By default, no subgroup checks are performed. -pub(super) fn extract_g2_input(input: &[u8]) -> Result { - _extract_g2_input(input, true) -} -/// Extracts a G2 point in Affine format from a 256 byte slice representation -/// without performing a subgroup check. -/// -/// Note: Skipping subgroup checks can introduce security issues. -/// This method should only be called if: -/// - The EIP specifies that no subgroup check should be performed -/// - One can be certain that the point is in the correct subgroup. -pub(super) fn extract_g2_input_no_subgroup_check( - input: &[u8], -) -> Result { - _extract_g2_input(input, false) -} -/// Extracts a G2 point in Affine format from a 256 byte slice representation. -/// -/// **Note**: This function will perform a G2 subgroup check if `subgroup_check` is set to `true`. -fn _extract_g2_input( - input: &[u8], - subgroup_check: bool, -) -> Result { - if input.len() != PADDED_G2_LENGTH { - return Err(PrecompileError::Other(format!( - "Input should be {PADDED_G2_LENGTH} bytes, was {}", - input.len() - ))); - } - - let mut input_fps = [&[0; FP_LENGTH]; 4]; - for i in 0..4 { - input_fps[i] = remove_padding(&input[i * PADDED_FP_LENGTH..(i + 1) * PADDED_FP_LENGTH])?; - } - - let out = decode_and_check_g2(input_fps[0], input_fps[1], input_fps[2], input_fps[3])?; - - if subgroup_check { - // NB: Subgroup checks - // - // Scalar multiplications, MSMs and pairings MUST perform a subgroup check. - // - // Implementations SHOULD use the optimized subgroup check method: - // - // https://eips.ethereum.org/assets/eip-2537/fast_subgroup_checks - // - // On any input that fail the subgroup check, the precompile MUST return an error. - // - // As endomorphism acceleration requires input on the correct subgroup, implementers MAY - // use endomorphism acceleration. - if unsafe { !blst_p2_affine_in_g2(&out) } { - return Err(PrecompileError::Other("Element not in G2".to_string())); - } - } - Ok(out) -} From c2bb3e58be3e1d26082a70948a838930b0bc49ef Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Wed, 26 Mar 2025 09:49:59 +0000 Subject: [PATCH 03/13] deserialization methods have been moved into blst wrapper --- crates/precompile/src/bls12_381/utils.rs | 65 +----------------------- 1 file changed, 2 insertions(+), 63 deletions(-) diff --git a/crates/precompile/src/bls12_381/utils.rs b/crates/precompile/src/bls12_381/utils.rs index 9168db6ee4..1ffe31999c 100644 --- a/crates/precompile/src/bls12_381/utils.rs +++ b/crates/precompile/src/bls12_381/utils.rs @@ -1,21 +1,7 @@ -use crate::bls12_381_const::{FP_LENGTH, FP_PAD_BY, MODULUS_REPR, PADDED_FP_LENGTH, SCALAR_LENGTH}; +use crate::bls12_381_const::{FP_LENGTH, FP_PAD_BY, MODULUS_REPR, PADDED_FP_LENGTH}; use crate::PrecompileError; -use blst::{ - blst_bendian_from_fp, blst_fp, blst_fp_from_bendian, blst_scalar, blst_scalar_from_bendian, -}; use core::cmp::Ordering; -/// Encodes a single finite field element into byte slice with padding. -pub(super) fn fp_to_bytes(out: &mut [u8], input: *const blst_fp) { - if out.len() != PADDED_FP_LENGTH { - return; - } - let (padding, rest) = out.split_at_mut(FP_PAD_BY); - padding.fill(0); - // SAFETY: Out length is checked previously, `input` is a blst value. - unsafe { blst_bendian_from_fp(rest.as_mut_ptr(), input) }; -} - /// Removes zeros with which the precompile inputs are left padded to 64 bytes. pub(super) fn remove_padding(input: &[u8]) -> Result<&[u8; FP_LENGTH], PrecompileError> { if input.len() != PADDED_FP_LENGTH { @@ -33,39 +19,8 @@ pub(super) fn remove_padding(input: &[u8]) -> Result<&[u8; FP_LENGTH], Precompil Ok(unpadded.try_into().unwrap()) } -/// Extracts a scalar from a 32 byte slice representation, decoding the input as a big endian -/// unsigned integer. If the input is not exactly 32 bytes long, an error is returned. -/// -/// From [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537): -/// * A scalar for the multiplication operation is encoded as 32 bytes by performing BigEndian -/// encoding of the corresponding (unsigned) integer. -/// -/// We do not check that the scalar is a canonical Fr element, because the EIP specifies: -/// * The corresponding integer is not required to be less than or equal than main subgroup order -/// `q`. -pub(super) fn extract_scalar_input(input: &[u8]) -> Result { - if input.len() != SCALAR_LENGTH { - return Err(PrecompileError::Other(format!( - "Input should be {SCALAR_LENGTH} bytes, was {}", - input.len() - ))); - } - - let mut out = blst_scalar::default(); - // SAFETY: `input` length is checked previously, out is a blst value. - unsafe { - // Note: We do not use `blst_scalar_fr_check` here because, from EIP-2537: - // - // * The corresponding integer is not required to be less than or equal than main subgroup - // order `q`. - blst_scalar_from_bendian(&mut out, input.as_ptr()) - }; - - Ok(out) -} - /// Checks if the input is a valid big-endian representation of a field element. -fn is_valid_be(input: &[u8; 48]) -> bool { +pub(super) fn is_valid_be(input: &[u8; 48]) -> bool { for (i, modulo) in input.iter().zip(MODULUS_REPR.iter()) { match i.cmp(modulo) { Ordering::Greater => return false, @@ -76,19 +31,3 @@ fn is_valid_be(input: &[u8; 48]) -> bool { // Return false if matching the modulus false } - -/// Checks whether or not the input represents a canonical field element, returning the field -/// element if successful. -pub(super) fn fp_from_bendian(input: &[u8; 48]) -> Result { - if !is_valid_be(input) { - return Err(PrecompileError::Other("non-canonical fp value".to_string())); - } - let mut fp = blst_fp::default(); - // SAFETY: `input` has fixed length, and `fp` is a blst value. - unsafe { - // This performs the check for canonical field elements - blst_fp_from_bendian(&mut fp, input.as_ptr()); - } - - Ok(fp) -} From ff5654a0e6e75f0fd1d93e81d1c5f3f1254efffc Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Wed, 26 Mar 2025 09:50:14 +0000 Subject: [PATCH 04/13] refactor precompiles to use blst wrapper --- crates/precompile/src/bls12_381/g1_add.rs | 8 +++----- crates/precompile/src/bls12_381/g1_msm.rs | 14 ++++---------- crates/precompile/src/bls12_381/g2_add.rs | 8 +++----- crates/precompile/src/bls12_381/g2_msm.rs | 14 ++++---------- crates/precompile/src/bls12_381/map_fp2_to_g2.rs | 7 +++++-- crates/precompile/src/bls12_381/map_fp_to_g1.rs | 10 ++++------ crates/precompile/src/bls12_381/pairing.rs | 2 +- 7 files changed, 24 insertions(+), 39 deletions(-) diff --git a/crates/precompile/src/bls12_381/g1_add.rs b/crates/precompile/src/bls12_381/g1_add.rs index 1fb12dc509..50d9c92262 100644 --- a/crates/precompile/src/bls12_381/g1_add.rs +++ b/crates/precompile/src/bls12_381/g1_add.rs @@ -1,10 +1,8 @@ -use super::blst::p1_add_affine; -use super::g1::{encode_g1_point, extract_g1_input_no_subgroup_check}; +use super::blst::{encode_g1_point, extract_g1_input_no_subgroup_check, p1_add_affine}; use crate::bls12_381_const::{ G1_ADD_ADDRESS, G1_ADD_BASE_GAS_FEE, G1_ADD_INPUT_LENGTH, PADDED_G1_LENGTH, }; -use crate::PrecompileWithAddress; -use crate::{PrecompileError, PrecompileOutput, PrecompileResult}; +use crate::{PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress}; use primitives::Bytes; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G1ADD precompile. @@ -36,5 +34,5 @@ pub(super) fn g1_add(input: &Bytes, gas_limit: u64) -> PrecompileResult { let p_aff = p1_add_affine(a_aff, b_aff); let out = encode_g1_point(&p_aff); - Ok(PrecompileOutput::new(G1_ADD_BASE_GAS_FEE, out)) + Ok(PrecompileOutput::new(G1_ADD_BASE_GAS_FEE, out.into())) } diff --git a/crates/precompile/src/bls12_381/g1_msm.rs b/crates/precompile/src/bls12_381/g1_msm.rs index fdda2613fd..bbf1c2e586 100644 --- a/crates/precompile/src/bls12_381/g1_msm.rs +++ b/crates/precompile/src/bls12_381/g1_msm.rs @@ -1,16 +1,10 @@ -use super::{ - blst::p1_msm, - g1::{encode_g1_point, extract_g1_input}, - utils::extract_scalar_input, -}; +use super::blst::{encode_g1_point, extract_g1_input, extract_scalar_input, p1_msm}; use crate::bls12_381_const::{ DISCOUNT_TABLE_G1_MSM, G1_MSM_ADDRESS, G1_MSM_BASE_GAS_FEE, G1_MSM_INPUT_LENGTH, NBITS, PADDED_G1_LENGTH, SCALAR_LENGTH, }; use crate::bls12_381_utils::msm_required_gas; -use crate::PrecompileWithAddress; -use crate::{PrecompileError, PrecompileOutput, PrecompileResult}; -use blst::blst_p1_affine; +use crate::{PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress}; use primitives::Bytes; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G1MSM precompile. @@ -39,7 +33,7 @@ pub(super) fn g1_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { return Err(PrecompileError::OutOfGas); } - let mut g1_points: Vec = Vec::with_capacity(k); + let mut g1_points: Vec<_> = Vec::with_capacity(k); let mut scalars_bytes: Vec = Vec::with_capacity(k * SCALAR_LENGTH); for i in 0..k { let encoded_g1_element = @@ -84,7 +78,7 @@ pub(super) fn g1_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { let multiexp_aff = p1_msm(g1_points, scalars_bytes, NBITS); let out = encode_g1_point(&multiexp_aff); - Ok(PrecompileOutput::new(required_gas, out)) + Ok(PrecompileOutput::new(required_gas, out.into())) } #[cfg(test)] diff --git a/crates/precompile/src/bls12_381/g2_add.rs b/crates/precompile/src/bls12_381/g2_add.rs index 301c85b17e..a0ae127c22 100644 --- a/crates/precompile/src/bls12_381/g2_add.rs +++ b/crates/precompile/src/bls12_381/g2_add.rs @@ -1,10 +1,8 @@ -use super::blst::p2_add_affine; -use super::g2::{encode_g2_point, extract_g2_input_no_subgroup_check}; +use super::blst::{encode_g2_point, extract_g2_input_no_subgroup_check, p2_add_affine}; use crate::bls12_381_const::{ G2_ADD_ADDRESS, G2_ADD_BASE_GAS_FEE, G2_ADD_INPUT_LENGTH, PADDED_G2_LENGTH, }; -use crate::PrecompileWithAddress; -use crate::{PrecompileError, PrecompileOutput, PrecompileResult}; +use crate::{PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress}; use primitives::Bytes; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G2ADD precompile. @@ -39,5 +37,5 @@ pub(super) fn g2_add(input: &Bytes, gas_limit: u64) -> PrecompileResult { let p_aff = p2_add_affine(a_aff, b_aff); let out = encode_g2_point(&p_aff); - Ok(PrecompileOutput::new(G2_ADD_BASE_GAS_FEE, out)) + Ok(PrecompileOutput::new(G2_ADD_BASE_GAS_FEE, out.into())) } diff --git a/crates/precompile/src/bls12_381/g2_msm.rs b/crates/precompile/src/bls12_381/g2_msm.rs index 2ca118935b..86b37d3b15 100644 --- a/crates/precompile/src/bls12_381/g2_msm.rs +++ b/crates/precompile/src/bls12_381/g2_msm.rs @@ -1,16 +1,10 @@ -use super::{ - blst::p2_msm, - g2::{encode_g2_point, extract_g2_input}, - utils::extract_scalar_input, -}; +use super::blst::{encode_g2_point, extract_g2_input, extract_scalar_input, p2_msm}; use crate::bls12_381_const::{ DISCOUNT_TABLE_G2_MSM, G2_MSM_ADDRESS, G2_MSM_BASE_GAS_FEE, G2_MSM_INPUT_LENGTH, NBITS, PADDED_G2_LENGTH, SCALAR_LENGTH, }; use crate::bls12_381_utils::msm_required_gas; -use crate::PrecompileWithAddress; -use crate::{PrecompileError, PrecompileOutput, PrecompileResult}; -use blst::blst_p2_affine; +use crate::{PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress}; use primitives::Bytes; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G2MSM precompile. @@ -39,7 +33,7 @@ pub(super) fn g2_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { return Err(PrecompileError::OutOfGas); } - let mut g2_points: Vec = Vec::with_capacity(k); + let mut g2_points: Vec<_> = Vec::with_capacity(k); let mut scalars_bytes: Vec = Vec::with_capacity(k * SCALAR_LENGTH); for i in 0..k { let encoded_g2_elements = @@ -82,5 +76,5 @@ pub(super) fn g2_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { let multiexp_aff = p2_msm(g2_points, scalars_bytes, NBITS); let out = encode_g2_point(&multiexp_aff); - Ok(PrecompileOutput::new(required_gas, out)) + Ok(PrecompileOutput::new(required_gas, out.into())) } diff --git a/crates/precompile/src/bls12_381/map_fp2_to_g2.rs b/crates/precompile/src/bls12_381/map_fp2_to_g2.rs index 6acd1469fb..f71ed7fcbb 100644 --- a/crates/precompile/src/bls12_381/map_fp2_to_g2.rs +++ b/crates/precompile/src/bls12_381/map_fp2_to_g2.rs @@ -1,5 +1,5 @@ use super::{ - blst::map_fp2_to_g2 as blst_map_fp2_to_g2, g2::check_canonical_fp2, g2::encode_g2_point, + blst::{check_canonical_fp2, encode_g2_point, map_fp2_to_g2 as blst_map_fp2_to_g2}, utils::remove_padding, }; use crate::bls12_381_const::{ @@ -35,5 +35,8 @@ pub(super) fn map_fp2_to_g2(input: &Bytes, gas_limit: u64) -> PrecompileResult { let p_aff = blst_map_fp2_to_g2(&fp2); let out = encode_g2_point(&p_aff); - Ok(PrecompileOutput::new(MAP_FP2_TO_G2_BASE_GAS_FEE, out)) + Ok(PrecompileOutput::new( + MAP_FP2_TO_G2_BASE_GAS_FEE, + out.into(), + )) } diff --git a/crates/precompile/src/bls12_381/map_fp_to_g1.rs b/crates/precompile/src/bls12_381/map_fp_to_g1.rs index 33db502c70..b46eeb9f67 100644 --- a/crates/precompile/src/bls12_381/map_fp_to_g1.rs +++ b/crates/precompile/src/bls12_381/map_fp_to_g1.rs @@ -1,11 +1,9 @@ use super::{ - blst::map_fp_to_g1 as blst_map_fp_to_g1, - g1::encode_g1_point, - utils::{fp_from_bendian, remove_padding}, + blst::{encode_g1_point, fp_from_bendian, map_fp_to_g1 as blst_map_fp_to_g1}, + utils::remove_padding, }; use crate::bls12_381_const::{MAP_FP_TO_G1_ADDRESS, MAP_FP_TO_G1_BASE_GAS_FEE, PADDED_FP_LENGTH}; -use crate::PrecompileWithAddress; -use crate::{PrecompileError, PrecompileOutput, PrecompileResult}; +use crate::{PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress}; use primitives::Bytes; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_MAP_FP_TO_G1 precompile. @@ -32,7 +30,7 @@ pub(super) fn map_fp_to_g1(input: &Bytes, gas_limit: u64) -> PrecompileResult { let p_aff = blst_map_fp_to_g1(&fp); let out = encode_g1_point(&p_aff); - Ok(PrecompileOutput::new(MAP_FP_TO_G1_BASE_GAS_FEE, out)) + Ok(PrecompileOutput::new(MAP_FP_TO_G1_BASE_GAS_FEE, out.into())) } #[cfg(test)] diff --git a/crates/precompile/src/bls12_381/pairing.rs b/crates/precompile/src/bls12_381/pairing.rs index 765a0946d1..08cd5960a9 100644 --- a/crates/precompile/src/bls12_381/pairing.rs +++ b/crates/precompile/src/bls12_381/pairing.rs @@ -1,4 +1,4 @@ -use super::{blst::pairing_check, g1::extract_g1_input, g2::extract_g2_input}; +use super::blst::{extract_g1_input, extract_g2_input, pairing_check}; use crate::bls12_381_const::{ PADDED_G1_LENGTH, PADDED_G2_LENGTH, PAIRING_ADDRESS, PAIRING_INPUT_LENGTH, PAIRING_MULTIPLIER_BASE, PAIRING_OFFSET_BASE, From b2277492ea58dc652bce33ebc603cd5796797bc8 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Wed, 26 Mar 2025 10:34:22 +0000 Subject: [PATCH 05/13] move methods that remove padding to precompile layer --- crates/precompile/src/bls12_381/blst.rs | 81 +++++++++++------------- crates/precompile/src/bls12_381/utils.rs | 37 ++++++++++- 2 files changed, 73 insertions(+), 45 deletions(-) diff --git a/crates/precompile/src/bls12_381/blst.rs b/crates/precompile/src/bls12_381/blst.rs index 692e7b6f25..d8d6c4b38c 100644 --- a/crates/precompile/src/bls12_381/blst.rs +++ b/crates/precompile/src/bls12_381/blst.rs @@ -1,7 +1,7 @@ // This module contains a safe wrapper around the blst library. use crate::{ - bls12_381::utils::{is_valid_be, remove_padding}, + bls12_381::utils::is_valid_be, bls12_381_const::{ FP_LENGTH, FP_PAD_BY, PADDED_FP_LENGTH, PADDED_G1_LENGTH, PADDED_G2_LENGTH, SCALAR_LENGTH, }, @@ -352,8 +352,8 @@ fn fp_to_bytes(out: &mut [u8], input: *const blst_fp) { /// See [fp_from_bendian] for more information. /// - If the point is not on the curve, an error is returned. fn decode_and_check_g1( - p0_x: &[u8; 48], - p0_y: &[u8; 48], + p0_x: &[u8; FP_LENGTH], + p0_y: &[u8; FP_LENGTH], ) -> Result { let out = blst_p1_affine { x: fp_from_bendian(p0_x)?, @@ -379,8 +379,11 @@ fn decode_and_check_g1( /// Extracts a G1 point in Affine format from a 128 byte slice representation. /// /// Note: By default, subgroup checks are performed. -pub(super) fn extract_g1_input(input: &[u8]) -> Result { - _extract_g1_input(input, true) +pub(super) fn extract_g1_input( + x: &[u8; FP_LENGTH], + y: &[u8; FP_LENGTH], +) -> Result { + _extract_g1_input(x, y, true) } /// Extracts a G1 point in Affine format from a 128 byte slice representation. /// without performing a subgroup check. @@ -390,27 +393,20 @@ pub(super) fn extract_g1_input(input: &[u8]) -> Result Result { - _extract_g1_input(input, false) + _extract_g1_input(x, y, false) } /// Extracts a G1 point in Affine format from a 128 byte slice representation. /// /// **Note**: This function will perform a G1 subgroup check if `subgroup_check` is set to `true`. fn _extract_g1_input( - input: &[u8], + x: &[u8; FP_LENGTH], + y: &[u8; FP_LENGTH], subgroup_check: bool, ) -> Result { - if input.len() != PADDED_G1_LENGTH { - return Err(PrecompileError::Other(format!( - "Input should be {PADDED_G1_LENGTH} bytes, was {}", - input.len() - ))); - } - - let input_p0_x = remove_padding(&input[..PADDED_FP_LENGTH])?; - let input_p0_y = remove_padding(&input[PADDED_FP_LENGTH..PADDED_G1_LENGTH])?; - let out = decode_and_check_g1(input_p0_x, input_p0_y)?; + let out = decode_and_check_g1(x, y)?; if subgroup_check { // NB: Subgroup checks @@ -458,10 +454,10 @@ pub(super) fn encode_g2_point(input: &blst_p2_affine) -> [u8; PADDED_G2_LENGTH] /// See [check_canonical_fp2] for more information. /// - If the point is not on the curve, an error is returned. fn decode_and_check_g2( - x1: &[u8; 48], - x2: &[u8; 48], - y1: &[u8; 48], - y2: &[u8; 48], + x1: &[u8; FP_LENGTH], + x2: &[u8; FP_LENGTH], + y1: &[u8; FP_LENGTH], + y2: &[u8; FP_LENGTH], ) -> Result { let out = blst_p2_affine { x: check_canonical_fp2(x1, x2)?, @@ -487,8 +483,8 @@ fn decode_and_check_g2( /// Checks whether or not the input represents a canonical fp2 field element, returning the field /// element if successful. pub(super) fn check_canonical_fp2( - input_1: &[u8; 48], - input_2: &[u8; 48], + input_1: &[u8; FP_LENGTH], + input_2: &[u8; FP_LENGTH], ) -> Result { let fp_1 = fp_from_bendian(input_1)?; let fp_2 = fp_from_bendian(input_2)?; @@ -500,8 +496,13 @@ pub(super) fn check_canonical_fp2( /// Extracts a G2 point in Affine format from a 256 byte slice representation. /// /// Note: By default, no subgroup checks are performed. -pub(super) fn extract_g2_input(input: &[u8]) -> Result { - _extract_g2_input(input, true) +pub(super) fn extract_g2_input( + a_x_0: &[u8; FP_LENGTH], + a_x_1: &[u8; FP_LENGTH], + a_y_0: &[u8; FP_LENGTH], + a_y_1: &[u8; FP_LENGTH], +) -> Result { + _extract_g2_input(a_x_0, a_x_1, a_y_0, a_y_1, true) } /// Extracts a G2 point in Affine format from a 256 byte slice representation /// without performing a subgroup check. @@ -511,30 +512,24 @@ pub(super) fn extract_g2_input(input: &[u8]) -> Result Result { - _extract_g2_input(input, false) + _extract_g2_input(a_x_0, a_x_1, a_y_0, a_y_1, false) } /// Extracts a G2 point in Affine format from a 256 byte slice representation. /// /// **Note**: This function will perform a G2 subgroup check if `subgroup_check` is set to `true`. fn _extract_g2_input( - input: &[u8], + a_x_0: &[u8; FP_LENGTH], + a_x_1: &[u8; FP_LENGTH], + a_y_0: &[u8; FP_LENGTH], + a_y_1: &[u8; FP_LENGTH], subgroup_check: bool, ) -> Result { - if input.len() != PADDED_G2_LENGTH { - return Err(PrecompileError::Other(format!( - "Input should be {PADDED_G2_LENGTH} bytes, was {}", - input.len() - ))); - } - - let mut input_fps = [&[0; FP_LENGTH]; 4]; - for i in 0..4 { - input_fps[i] = remove_padding(&input[i * PADDED_FP_LENGTH..(i + 1) * PADDED_FP_LENGTH])?; - } - - let out = decode_and_check_g2(input_fps[0], input_fps[1], input_fps[2], input_fps[3])?; + let out = decode_and_check_g2(a_x_0, a_x_1, a_y_0, a_y_1)?; if subgroup_check { // NB: Subgroup checks @@ -558,7 +553,7 @@ fn _extract_g2_input( /// Checks whether or not the input represents a canonical field element, returning the field /// element if successful. -pub(super) fn fp_from_bendian(input: &[u8; 48]) -> Result { +pub(super) fn fp_from_bendian(input: &[u8; FP_LENGTH]) -> Result { if !is_valid_be(input) { return Err(PrecompileError::Other("non-canonical fp value".to_string())); } diff --git a/crates/precompile/src/bls12_381/utils.rs b/crates/precompile/src/bls12_381/utils.rs index 1ffe31999c..d898588d1c 100644 --- a/crates/precompile/src/bls12_381/utils.rs +++ b/crates/precompile/src/bls12_381/utils.rs @@ -1,9 +1,11 @@ -use crate::bls12_381_const::{FP_LENGTH, FP_PAD_BY, MODULUS_REPR, PADDED_FP_LENGTH}; +use crate::bls12_381_const::{ + FP_LENGTH, FP_PAD_BY, MODULUS_REPR, PADDED_FP_LENGTH, PADDED_G1_LENGTH, PADDED_G2_LENGTH, +}; use crate::PrecompileError; use core::cmp::Ordering; /// Removes zeros with which the precompile inputs are left padded to 64 bytes. -pub(super) fn remove_padding(input: &[u8]) -> Result<&[u8; FP_LENGTH], PrecompileError> { +pub(super) fn remove_fp_padding(input: &[u8]) -> Result<&[u8; FP_LENGTH], PrecompileError> { if input.len() != PADDED_FP_LENGTH { return Err(PrecompileError::Other(format!( "Padded input should be {PADDED_FP_LENGTH} bytes, was {}", @@ -18,6 +20,37 @@ pub(super) fn remove_padding(input: &[u8]) -> Result<&[u8; FP_LENGTH], Precompil } Ok(unpadded.try_into().unwrap()) } +/// remove_g1_padding removes the padding applied to the Fp elements that constitute the +/// encoded G1 element. +pub(super) fn remove_g1_padding(input: &[u8]) -> Result<[&[u8; 48]; 2], PrecompileError> { + if input.len() != PADDED_G1_LENGTH { + return Err(PrecompileError::Other(format!( + "Input should be {PADDED_G1_LENGTH} bytes, was {}", + input.len() + ))); + } + + let x = remove_fp_padding(&input[..PADDED_FP_LENGTH])?; + let y = remove_fp_padding(&input[PADDED_FP_LENGTH..PADDED_G1_LENGTH])?; + Ok([x, y]) +} + +/// remove_g2_padding removes the padding applied to the Fp elements that constitute the +/// encoded G2 element. +pub(super) fn remove_g2_padding(input: &[u8]) -> Result<[&[u8; 48]; 4], PrecompileError> { + if input.len() != PADDED_G2_LENGTH { + return Err(PrecompileError::Other(format!( + "Input should be {PADDED_G2_LENGTH} bytes, was {}", + input.len() + ))); + } + + let mut input_fps = [&[0; FP_LENGTH]; 4]; + for i in 0..4 { + input_fps[i] = remove_fp_padding(&input[i * PADDED_FP_LENGTH..(i + 1) * PADDED_FP_LENGTH])?; + } + Ok(input_fps) +} /// Checks if the input is a valid big-endian representation of a field element. pub(super) fn is_valid_be(input: &[u8; 48]) -> bool { From 0ff0a08af24b09b080eedcfbeac7225b71c55139 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Wed, 26 Mar 2025 10:34:38 +0000 Subject: [PATCH 06/13] refactor precompile code --- crates/precompile/src/bls12_381/g1_add.rs | 8 ++++++-- crates/precompile/src/bls12_381/g1_msm.rs | 6 +++++- crates/precompile/src/bls12_381/g2_add.rs | 8 ++++++-- crates/precompile/src/bls12_381/g2_msm.rs | 10 +++++++--- crates/precompile/src/bls12_381/map_fp2_to_g2.rs | 6 +++--- crates/precompile/src/bls12_381/map_fp_to_g1.rs | 4 ++-- crates/precompile/src/bls12_381/pairing.rs | 8 ++++++-- 7 files changed, 35 insertions(+), 15 deletions(-) diff --git a/crates/precompile/src/bls12_381/g1_add.rs b/crates/precompile/src/bls12_381/g1_add.rs index 50d9c92262..dc334160ca 100644 --- a/crates/precompile/src/bls12_381/g1_add.rs +++ b/crates/precompile/src/bls12_381/g1_add.rs @@ -1,4 +1,5 @@ use super::blst::{encode_g1_point, extract_g1_input_no_subgroup_check, p1_add_affine}; +use super::utils::remove_g1_padding; use crate::bls12_381_const::{ G1_ADD_ADDRESS, G1_ADD_BASE_GAS_FEE, G1_ADD_INPUT_LENGTH, PADDED_G1_LENGTH, }; @@ -25,12 +26,15 @@ pub(super) fn g1_add(input: &Bytes, gas_limit: u64) -> PrecompileResult { ))); } + let [a_x, a_y] = remove_g1_padding(&input[..PADDED_G1_LENGTH])?; + let [b_x, b_y] = remove_g1_padding(&input[PADDED_G1_LENGTH..])?; + // NB: There is no subgroup check for the G1 addition precompile because the time to do the subgroup // check would be more than the time it takes to to do the g1 addition. // // Users should be careful to note whether the points being added are indeed in the right subgroup. - let a_aff = &extract_g1_input_no_subgroup_check(&input[..PADDED_G1_LENGTH])?; - let b_aff = &extract_g1_input_no_subgroup_check(&input[PADDED_G1_LENGTH..])?; + let a_aff = &extract_g1_input_no_subgroup_check(a_x, a_y)?; + let b_aff = &extract_g1_input_no_subgroup_check(b_x, b_y)?; let p_aff = p1_add_affine(a_aff, b_aff); let out = encode_g1_point(&p_aff); diff --git a/crates/precompile/src/bls12_381/g1_msm.rs b/crates/precompile/src/bls12_381/g1_msm.rs index bbf1c2e586..edffc4996d 100644 --- a/crates/precompile/src/bls12_381/g1_msm.rs +++ b/crates/precompile/src/bls12_381/g1_msm.rs @@ -1,4 +1,5 @@ use super::blst::{encode_g1_point, extract_g1_input, extract_scalar_input, p1_msm}; +use crate::bls12_381::utils::remove_g1_padding; use crate::bls12_381_const::{ DISCOUNT_TABLE_G1_MSM, G1_MSM_ADDRESS, G1_MSM_BASE_GAS_FEE, G1_MSM_INPUT_LENGTH, NBITS, PADDED_G1_LENGTH, SCALAR_LENGTH, @@ -48,8 +49,11 @@ pub(super) fn g1_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { if encoded_g1_element.iter().all(|i| *i == 0) { continue; } + + let [a_x, a_y] = remove_g1_padding(encoded_g1_element)?; + // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check. - let p0_aff = extract_g1_input(encoded_g1_element)?; + let p0_aff = extract_g1_input(a_x, a_y)?; // If the scalar is zero, then this is a no-op. // diff --git a/crates/precompile/src/bls12_381/g2_add.rs b/crates/precompile/src/bls12_381/g2_add.rs index a0ae127c22..73f9ce3d0a 100644 --- a/crates/precompile/src/bls12_381/g2_add.rs +++ b/crates/precompile/src/bls12_381/g2_add.rs @@ -1,4 +1,5 @@ use super::blst::{encode_g2_point, extract_g2_input_no_subgroup_check, p2_add_affine}; +use super::utils::remove_g2_padding; use crate::bls12_381_const::{ G2_ADD_ADDRESS, G2_ADD_BASE_GAS_FEE, G2_ADD_INPUT_LENGTH, PADDED_G2_LENGTH, }; @@ -26,12 +27,15 @@ pub(super) fn g2_add(input: &Bytes, gas_limit: u64) -> PrecompileResult { ))); } + let [a_x_0, a_x_1, a_y_0, a_y_1] = remove_g2_padding(&input[..PADDED_G2_LENGTH])?; + let [b_x_0, b_x_1, b_y_0, b_y_1] = remove_g2_padding(&input[PADDED_G2_LENGTH..])?; + // NB: There is no subgroup check for the G2 addition precompile because the time to do the subgroup // check would be more than the time it takes to to do the g1 addition. // // Users should be careful to note whether the points being added are indeed in the right subgroup. - let a_aff = &extract_g2_input_no_subgroup_check(&input[..PADDED_G2_LENGTH])?; - let b_aff = &extract_g2_input_no_subgroup_check(&input[PADDED_G2_LENGTH..])?; + let a_aff = &extract_g2_input_no_subgroup_check(a_x_0, a_x_1, a_y_0, a_y_1)?; + let b_aff = &extract_g2_input_no_subgroup_check(b_x_0, b_x_1, b_y_0, b_y_1)?; // Use the safe wrapper for G2 point addition let p_aff = p2_add_affine(a_aff, b_aff); diff --git a/crates/precompile/src/bls12_381/g2_msm.rs b/crates/precompile/src/bls12_381/g2_msm.rs index 86b37d3b15..e75830a532 100644 --- a/crates/precompile/src/bls12_381/g2_msm.rs +++ b/crates/precompile/src/bls12_381/g2_msm.rs @@ -1,4 +1,5 @@ use super::blst::{encode_g2_point, extract_g2_input, extract_scalar_input, p2_msm}; +use super::utils::remove_g2_padding; use crate::bls12_381_const::{ DISCOUNT_TABLE_G2_MSM, G2_MSM_ADDRESS, G2_MSM_BASE_GAS_FEE, G2_MSM_INPUT_LENGTH, NBITS, PADDED_G2_LENGTH, SCALAR_LENGTH, @@ -36,7 +37,7 @@ pub(super) fn g2_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { let mut g2_points: Vec<_> = Vec::with_capacity(k); let mut scalars_bytes: Vec = Vec::with_capacity(k * SCALAR_LENGTH); for i in 0..k { - let encoded_g2_elements = + let encoded_g2_element = &input[i * G2_MSM_INPUT_LENGTH..i * G2_MSM_INPUT_LENGTH + PADDED_G2_LENGTH]; let encoded_scalar = &input[i * G2_MSM_INPUT_LENGTH + PADDED_G2_LENGTH ..i * G2_MSM_INPUT_LENGTH + PADDED_G2_LENGTH + SCALAR_LENGTH]; @@ -44,13 +45,16 @@ pub(super) fn g2_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { // Filter out points infinity as an optimization, since it is a no-op. // Note: Previously, points were being batch converted from Jacobian to Affine. In `blst`, this would essentially, // zero out all of the points. Since all points are in affine, this bug is avoided. - if encoded_g2_elements.iter().all(|i| *i == 0) { + if encoded_g2_element.iter().all(|i| *i == 0) { continue; } + + let [a_x_0, a_x_1, a_y_0, a_y_1] = remove_g2_padding(encoded_g2_element)?; + // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check. // // So we set the subgroup_check flag to `true` - let p0_aff = extract_g2_input(encoded_g2_elements)?; + let p0_aff = extract_g2_input(a_x_0, a_x_1, a_y_0, a_y_1)?; // If the scalar is zero, then this is a no-op. // diff --git a/crates/precompile/src/bls12_381/map_fp2_to_g2.rs b/crates/precompile/src/bls12_381/map_fp2_to_g2.rs index f71ed7fcbb..31bf3a8172 100644 --- a/crates/precompile/src/bls12_381/map_fp2_to_g2.rs +++ b/crates/precompile/src/bls12_381/map_fp2_to_g2.rs @@ -1,6 +1,6 @@ use super::{ blst::{check_canonical_fp2, encode_g2_point, map_fp2_to_g2 as blst_map_fp2_to_g2}, - utils::remove_padding, + utils::remove_fp_padding, }; use crate::bls12_381_const::{ MAP_FP2_TO_G2_ADDRESS, MAP_FP2_TO_G2_BASE_GAS_FEE, PADDED_FP2_LENGTH, PADDED_FP_LENGTH, @@ -29,8 +29,8 @@ pub(super) fn map_fp2_to_g2(input: &Bytes, gas_limit: u64) -> PrecompileResult { ))); } - let input_p0_x = remove_padding(&input[..PADDED_FP_LENGTH])?; - let input_p0_y = remove_padding(&input[PADDED_FP_LENGTH..PADDED_FP2_LENGTH])?; + let input_p0_x = remove_fp_padding(&input[..PADDED_FP_LENGTH])?; + let input_p0_y = remove_fp_padding(&input[PADDED_FP_LENGTH..PADDED_FP2_LENGTH])?; let fp2 = check_canonical_fp2(input_p0_x, input_p0_y)?; let p_aff = blst_map_fp2_to_g2(&fp2); diff --git a/crates/precompile/src/bls12_381/map_fp_to_g1.rs b/crates/precompile/src/bls12_381/map_fp_to_g1.rs index b46eeb9f67..85d117abdd 100644 --- a/crates/precompile/src/bls12_381/map_fp_to_g1.rs +++ b/crates/precompile/src/bls12_381/map_fp_to_g1.rs @@ -1,6 +1,6 @@ use super::{ blst::{encode_g1_point, fp_from_bendian, map_fp_to_g1 as blst_map_fp_to_g1}, - utils::remove_padding, + utils::remove_fp_padding, }; use crate::bls12_381_const::{MAP_FP_TO_G1_ADDRESS, MAP_FP_TO_G1_BASE_GAS_FEE, PADDED_FP_LENGTH}; use crate::{PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress}; @@ -25,7 +25,7 @@ pub(super) fn map_fp_to_g1(input: &Bytes, gas_limit: u64) -> PrecompileResult { ))); } - let input_p0 = remove_padding(input)?; + let input_p0 = remove_fp_padding(input)?; let fp = fp_from_bendian(input_p0)?; let p_aff = blst_map_fp_to_g1(&fp); diff --git a/crates/precompile/src/bls12_381/pairing.rs b/crates/precompile/src/bls12_381/pairing.rs index 08cd5960a9..64686dbcca 100644 --- a/crates/precompile/src/bls12_381/pairing.rs +++ b/crates/precompile/src/bls12_381/pairing.rs @@ -1,4 +1,5 @@ use super::blst::{extract_g1_input, extract_g2_input, pairing_check}; +use super::utils::{remove_g1_padding, remove_g2_padding}; use crate::bls12_381_const::{ PADDED_G1_LENGTH, PADDED_G2_LENGTH, PAIRING_ADDRESS, PAIRING_INPUT_LENGTH, PAIRING_MULTIPLIER_BASE, PAIRING_OFFSET_BASE, @@ -55,10 +56,13 @@ pub(super) fn pairing(input: &Bytes, gas_limit: u64) -> PrecompileResult { let g1_is_zero = encoded_g1_element.iter().all(|i| *i == 0); let g2_is_zero = encoded_g2_element.iter().all(|i| *i == 0); + let [a_x, a_y] = remove_g1_padding(encoded_g1_element)?; + let [b_x_0, b_x_1, b_y_0, b_y_1] = remove_g2_padding(encoded_g2_element)?; + // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check. // extract_g1_input and extract_g2_input perform the necessary checks - let p1_aff = extract_g1_input(encoded_g1_element)?; - let p2_aff = extract_g2_input(encoded_g2_element)?; + let p1_aff = extract_g1_input(a_x, a_y)?; + let p2_aff = extract_g2_input(b_x_0, b_x_1, b_y_0, b_y_1)?; if !g1_is_zero & !g2_is_zero { pairs.push((p1_aff, p2_aff)); From d5108c52a0c1d95bee82aea6c6bc7be7bd312c2c Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Wed, 26 Mar 2025 10:53:52 +0000 Subject: [PATCH 07/13] function and comment refactoring --- crates/precompile/src/bls12_381/blst.rs | 98 ++++++++++++------- crates/precompile/src/bls12_381/g1_add.rs | 6 +- crates/precompile/src/bls12_381/g1_msm.rs | 6 +- crates/precompile/src/bls12_381/g2_add.rs | 6 +- crates/precompile/src/bls12_381/g2_msm.rs | 6 +- .../precompile/src/bls12_381/map_fp2_to_g2.rs | 4 +- .../precompile/src/bls12_381/map_fp_to_g1.rs | 4 +- crates/precompile/src/bls12_381/pairing.rs | 6 +- 8 files changed, 80 insertions(+), 56 deletions(-) diff --git a/crates/precompile/src/bls12_381/blst.rs b/crates/precompile/src/bls12_381/blst.rs index d8d6c4b38c..72957c2f11 100644 --- a/crates/precompile/src/bls12_381/blst.rs +++ b/crates/precompile/src/bls12_381/blst.rs @@ -97,6 +97,8 @@ pub(super) fn p2_add_affine(a: &blst_p2_affine, b: &blst_p2_affine) -> blst_p2_a /// /// Takes a G1 point in affine form and a scalar, and returns the result /// of the scalar multiplication in affine form +/// +/// Note: The scalar is expected to be in Big Endian format. #[inline] fn p1_scalar_mul(p: &blst_p1_affine, scalar: &[u8]) -> blst_p1_affine { // Convert point to Jacobian coordinates @@ -115,6 +117,8 @@ fn p1_scalar_mul(p: &blst_p1_affine, scalar: &[u8]) -> blst_p1_affine { /// /// Takes a G2 point in affine form and a scalar, and returns the result /// of the scalar multiplication in affine form +/// +/// Note: The scalar is expected to be in Big Endian format. #[inline] fn p2_scalar_mul(p: &blst_p2_affine, scalar: &[u8]) -> blst_p2_affine { // Convert point to Jacobian coordinates @@ -132,7 +136,8 @@ fn p2_scalar_mul(p: &blst_p2_affine, scalar: &[u8]) -> blst_p2_affine { /// /// Takes a vector of G1 points and corresponding scalars, and returns their weighted sum /// -/// Note: This method assumes that `g1_points` does not contain any points at infinity. +/// Note: Scalars are expected to be in Big Endian format. +/// This method assumes that `g1_points` does not contain any points at infinity. #[inline] pub(super) fn p1_msm( g1_points: Vec, @@ -176,7 +181,8 @@ pub(super) fn p1_msm( /// /// Takes a vector of G2 points and corresponding scalars, and returns their weighted sum /// -/// Note: This method assumes that `g2_points` does not contain any points at infinity. +/// Note: Scalars are expected to be in Big Endian format. +/// This method assumes that `g2_points` does not contain any points at infinity. #[inline] pub(super) fn p2_msm( g2_points: Vec, @@ -324,6 +330,8 @@ pub(super) fn pairing_check(pairs: &[(blst_p1_affine, blst_p2_affine)]) -> bool } /// Encodes a G1 point in affine format into byte slice with padded elements. +/// +/// Note: The encoded bytes are in Big Endian format. pub(super) fn encode_g1_point(input: *const blst_p1_affine) -> [u8; PADDED_G1_LENGTH] { let mut out = [0u8; PADDED_G1_LENGTH]; // SAFETY: Out comes from fixed length array, input is a blst value. @@ -335,6 +343,8 @@ pub(super) fn encode_g1_point(input: *const blst_p1_affine) -> [u8; PADDED_G1_LE } /// Encodes a single finite field element into byte slice with padding. +/// +/// Note: The encoded bytes are in Big Endian format. fn fp_to_bytes(out: &mut [u8], input: *const blst_fp) { if out.len() != PADDED_FP_LENGTH { return; @@ -348,16 +358,18 @@ fn fp_to_bytes(out: &mut [u8], input: *const blst_fp) { /// Returns a `blst_p1_affine` from the provided byte slices, which represent the x and y /// affine coordinates of the point. /// +/// Note: Coordinates are expected to be in Big Endian format. +/// /// - If the x or y coordinate do not represent a canonical field element, an error is returned. -/// See [fp_from_bendian] for more information. +/// See [read_fp] for more information. /// - If the point is not on the curve, an error is returned. -fn decode_and_check_g1( +fn decode_g1_on_curve( p0_x: &[u8; FP_LENGTH], p0_y: &[u8; FP_LENGTH], ) -> Result { let out = blst_p1_affine { - x: fp_from_bendian(p0_x)?, - y: fp_from_bendian(p0_y)?, + x: read_fp(p0_x)?, + y: read_fp(p0_y)?, }; // From EIP-2537: @@ -376,37 +388,40 @@ fn decode_and_check_g1( Ok(out) } -/// Extracts a G1 point in Affine format from a 128 byte slice representation. +/// Extracts a G1 point in Affine format from the x and y coordinates. /// -/// Note: By default, subgroup checks are performed. -pub(super) fn extract_g1_input( +/// Note: Coordinates are expected to be in Big Endian format. +/// By default, subgroup checks are performed. +pub(super) fn read_g1( x: &[u8; FP_LENGTH], y: &[u8; FP_LENGTH], ) -> Result { _extract_g1_input(x, y, true) } -/// Extracts a G1 point in Affine format from a 128 byte slice representation. +/// Extracts a G1 point in Affine format from the x and y coordinates /// without performing a subgroup check. /// -/// Note: Skipping subgroup checks can introduce security issues. +/// Note: Coordinates are expected to be in Big Endian format. +/// Skipping subgroup checks can introduce security issues. /// This method should only be called if: /// - The EIP specifies that no subgroup check should be performed /// - One can be certain that the point is in the correct subgroup. -pub(super) fn extract_g1_input_no_subgroup_check( +pub(super) fn read_g1_no_subgroup_check( x: &[u8; FP_LENGTH], y: &[u8; FP_LENGTH], ) -> Result { _extract_g1_input(x, y, false) } -/// Extracts a G1 point in Affine format from a 128 byte slice representation. +/// Extracts a G1 point in Affine format from the x and y coordinates. /// -/// **Note**: This function will perform a G1 subgroup check if `subgroup_check` is set to `true`. +/// Note: Coordinates are expected to be in Big Endian format. +/// This function will perform a G1 subgroup check if `subgroup_check` is set to `true`. fn _extract_g1_input( x: &[u8; FP_LENGTH], y: &[u8; FP_LENGTH], subgroup_check: bool, ) -> Result { - let out = decode_and_check_g1(x, y)?; + let out = decode_g1_on_curve(x, y)?; if subgroup_check { // NB: Subgroup checks @@ -429,6 +444,8 @@ fn _extract_g1_input( } /// Encodes a G2 point in affine format into byte slice with padded elements. +/// +/// Note: The encoded bytes are in Big Endian format. pub(super) fn encode_g2_point(input: &blst_p2_affine) -> [u8; PADDED_G2_LENGTH] { let mut out = [0u8; PADDED_G2_LENGTH]; fp_to_bytes(&mut out[..PADDED_FP_LENGTH], &input.x.fp[0]); @@ -450,18 +467,20 @@ pub(super) fn encode_g2_point(input: &blst_p2_affine) -> [u8; PADDED_G2_LENGTH] /// Returns a `blst_p2_affine` from the provided byte slices, which represent the x and y /// affine coordinates of the point. /// +/// Note: Coordinates are expected to be in Big Endian format. +/// /// - If the x or y coordinate do not represent a canonical field element, an error is returned. -/// See [check_canonical_fp2] for more information. +/// See [read_fp2] for more information. /// - If the point is not on the curve, an error is returned. -fn decode_and_check_g2( +fn decode_g2_on_curve( x1: &[u8; FP_LENGTH], x2: &[u8; FP_LENGTH], y1: &[u8; FP_LENGTH], y2: &[u8; FP_LENGTH], ) -> Result { let out = blst_p2_affine { - x: check_canonical_fp2(x1, x2)?, - y: check_canonical_fp2(y1, y2)?, + x: read_fp2(x1, x2)?, + y: read_fp2(y1, y2)?, }; // From EIP-2537: @@ -480,23 +499,25 @@ fn decode_and_check_g2( Ok(out) } -/// Checks whether or not the input represents a canonical fp2 field element, returning the field -/// element if successful. -pub(super) fn check_canonical_fp2( +/// Creates a blst_fp2 element from two field elements. +/// +/// Field elements are expected to be in Big Endian format. +/// Returns an error if either of the input field elements is not canonical. +pub(super) fn read_fp2( input_1: &[u8; FP_LENGTH], input_2: &[u8; FP_LENGTH], ) -> Result { - let fp_1 = fp_from_bendian(input_1)?; - let fp_2 = fp_from_bendian(input_2)?; + let fp_1 = read_fp(input_1)?; + let fp_2 = read_fp(input_2)?; let fp2 = blst_fp2 { fp: [fp_1, fp_2] }; Ok(fp2) } -/// Extracts a G2 point in Affine format from a 256 byte slice representation. +/// Extracts a G2 point in Affine format from the x and y coordinates. /// -/// Note: By default, no subgroup checks are performed. -pub(super) fn extract_g2_input( +/// Note: By default, subgroup checks are performed. +pub(super) fn read_g2( a_x_0: &[u8; FP_LENGTH], a_x_1: &[u8; FP_LENGTH], a_y_0: &[u8; FP_LENGTH], @@ -504,14 +525,14 @@ pub(super) fn extract_g2_input( ) -> Result { _extract_g2_input(a_x_0, a_x_1, a_y_0, a_y_1, true) } -/// Extracts a G2 point in Affine format from a 256 byte slice representation +/// Extracts a G2 point in Affine format from the x and y coordinates /// without performing a subgroup check. /// /// Note: Skipping subgroup checks can introduce security issues. /// This method should only be called if: /// - The EIP specifies that no subgroup check should be performed /// - One can be certain that the point is in the correct subgroup. -pub(super) fn extract_g2_input_no_subgroup_check( +pub(super) fn read_g2_no_subgroup_check( a_x_0: &[u8; FP_LENGTH], a_x_1: &[u8; FP_LENGTH], a_y_0: &[u8; FP_LENGTH], @@ -519,9 +540,10 @@ pub(super) fn extract_g2_input_no_subgroup_check( ) -> Result { _extract_g2_input(a_x_0, a_x_1, a_y_0, a_y_1, false) } -/// Extracts a G2 point in Affine format from a 256 byte slice representation. +/// Extracts a G2 point in Affine format from the x and y coordinates. /// -/// **Note**: This function will perform a G2 subgroup check if `subgroup_check` is set to `true`. +/// Note: Coordinates are expected to be in Big Endian format. +/// This function will perform a G2 subgroup check if `subgroup_check` is set to `true`. fn _extract_g2_input( a_x_0: &[u8; FP_LENGTH], a_x_1: &[u8; FP_LENGTH], @@ -529,7 +551,7 @@ fn _extract_g2_input( a_y_1: &[u8; FP_LENGTH], subgroup_check: bool, ) -> Result { - let out = decode_and_check_g2(a_x_0, a_x_1, a_y_0, a_y_1)?; + let out = decode_g2_on_curve(a_x_0, a_x_1, a_y_0, a_y_1)?; if subgroup_check { // NB: Subgroup checks @@ -551,9 +573,11 @@ fn _extract_g2_input( Ok(out) } -/// Checks whether or not the input represents a canonical field element, returning the field -/// element if successful. -pub(super) fn fp_from_bendian(input: &[u8; FP_LENGTH]) -> Result { +/// Checks whether or not the input represents a canonical field element +/// returning the field element if successful. +/// +/// Note: The field element is expected to be in big endian format. +pub(super) fn read_fp(input: &[u8; FP_LENGTH]) -> Result { if !is_valid_be(input) { return Err(PrecompileError::Other("non-canonical fp value".to_string())); } @@ -567,7 +591,7 @@ pub(super) fn fp_from_bendian(input: &[u8; FP_LENGTH]) -> Result Result Result { +pub(super) fn read_scalar(input: &[u8]) -> Result { if input.len() != SCALAR_LENGTH { return Err(PrecompileError::Other(format!( "Input should be {SCALAR_LENGTH} bytes, was {}", diff --git a/crates/precompile/src/bls12_381/g1_add.rs b/crates/precompile/src/bls12_381/g1_add.rs index dc334160ca..ab00cac035 100644 --- a/crates/precompile/src/bls12_381/g1_add.rs +++ b/crates/precompile/src/bls12_381/g1_add.rs @@ -1,4 +1,4 @@ -use super::blst::{encode_g1_point, extract_g1_input_no_subgroup_check, p1_add_affine}; +use super::blst::{encode_g1_point, p1_add_affine, read_g1_no_subgroup_check}; use super::utils::remove_g1_padding; use crate::bls12_381_const::{ G1_ADD_ADDRESS, G1_ADD_BASE_GAS_FEE, G1_ADD_INPUT_LENGTH, PADDED_G1_LENGTH, @@ -33,8 +33,8 @@ pub(super) fn g1_add(input: &Bytes, gas_limit: u64) -> PrecompileResult { // check would be more than the time it takes to to do the g1 addition. // // Users should be careful to note whether the points being added are indeed in the right subgroup. - let a_aff = &extract_g1_input_no_subgroup_check(a_x, a_y)?; - let b_aff = &extract_g1_input_no_subgroup_check(b_x, b_y)?; + let a_aff = &read_g1_no_subgroup_check(a_x, a_y)?; + let b_aff = &read_g1_no_subgroup_check(b_x, b_y)?; let p_aff = p1_add_affine(a_aff, b_aff); let out = encode_g1_point(&p_aff); diff --git a/crates/precompile/src/bls12_381/g1_msm.rs b/crates/precompile/src/bls12_381/g1_msm.rs index edffc4996d..b74ca81c2b 100644 --- a/crates/precompile/src/bls12_381/g1_msm.rs +++ b/crates/precompile/src/bls12_381/g1_msm.rs @@ -1,4 +1,4 @@ -use super::blst::{encode_g1_point, extract_g1_input, extract_scalar_input, p1_msm}; +use super::blst::{encode_g1_point, p1_msm, read_g1, read_scalar}; use crate::bls12_381::utils::remove_g1_padding; use crate::bls12_381_const::{ DISCOUNT_TABLE_G1_MSM, G1_MSM_ADDRESS, G1_MSM_BASE_GAS_FEE, G1_MSM_INPUT_LENGTH, NBITS, @@ -53,7 +53,7 @@ pub(super) fn g1_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { let [a_x, a_y] = remove_g1_padding(encoded_g1_element)?; // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check. - let p0_aff = extract_g1_input(a_x, a_y)?; + let p0_aff = read_g1(a_x, a_y)?; // If the scalar is zero, then this is a no-op. // @@ -66,7 +66,7 @@ pub(super) fn g1_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { g1_points.push(p0_aff); - scalars_bytes.extend_from_slice(&extract_scalar_input(encoded_scalar)?.b); + scalars_bytes.extend_from_slice(&read_scalar(encoded_scalar)?.b); } // Return the encoding for the point at the infinity according to EIP-2537 diff --git a/crates/precompile/src/bls12_381/g2_add.rs b/crates/precompile/src/bls12_381/g2_add.rs index 73f9ce3d0a..6edc1b57e7 100644 --- a/crates/precompile/src/bls12_381/g2_add.rs +++ b/crates/precompile/src/bls12_381/g2_add.rs @@ -1,4 +1,4 @@ -use super::blst::{encode_g2_point, extract_g2_input_no_subgroup_check, p2_add_affine}; +use super::blst::{encode_g2_point, p2_add_affine, read_g2_no_subgroup_check}; use super::utils::remove_g2_padding; use crate::bls12_381_const::{ G2_ADD_ADDRESS, G2_ADD_BASE_GAS_FEE, G2_ADD_INPUT_LENGTH, PADDED_G2_LENGTH, @@ -34,8 +34,8 @@ pub(super) fn g2_add(input: &Bytes, gas_limit: u64) -> PrecompileResult { // check would be more than the time it takes to to do the g1 addition. // // Users should be careful to note whether the points being added are indeed in the right subgroup. - let a_aff = &extract_g2_input_no_subgroup_check(a_x_0, a_x_1, a_y_0, a_y_1)?; - let b_aff = &extract_g2_input_no_subgroup_check(b_x_0, b_x_1, b_y_0, b_y_1)?; + let a_aff = &read_g2_no_subgroup_check(a_x_0, a_x_1, a_y_0, a_y_1)?; + let b_aff = &read_g2_no_subgroup_check(b_x_0, b_x_1, b_y_0, b_y_1)?; // Use the safe wrapper for G2 point addition let p_aff = p2_add_affine(a_aff, b_aff); diff --git a/crates/precompile/src/bls12_381/g2_msm.rs b/crates/precompile/src/bls12_381/g2_msm.rs index e75830a532..3543d66182 100644 --- a/crates/precompile/src/bls12_381/g2_msm.rs +++ b/crates/precompile/src/bls12_381/g2_msm.rs @@ -1,4 +1,4 @@ -use super::blst::{encode_g2_point, extract_g2_input, extract_scalar_input, p2_msm}; +use super::blst::{encode_g2_point, p2_msm, read_g2, read_scalar}; use super::utils::remove_g2_padding; use crate::bls12_381_const::{ DISCOUNT_TABLE_G2_MSM, G2_MSM_ADDRESS, G2_MSM_BASE_GAS_FEE, G2_MSM_INPUT_LENGTH, NBITS, @@ -54,7 +54,7 @@ pub(super) fn g2_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check. // // So we set the subgroup_check flag to `true` - let p0_aff = extract_g2_input(a_x_0, a_x_1, a_y_0, a_y_1)?; + let p0_aff = read_g2(a_x_0, a_x_1, a_y_0, a_y_1)?; // If the scalar is zero, then this is a no-op. // @@ -68,7 +68,7 @@ pub(super) fn g2_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { // Convert affine point to Jacobian coordinates using our helper function g2_points.push(p0_aff); - scalars_bytes.extend_from_slice(&extract_scalar_input(encoded_scalar)?.b); + scalars_bytes.extend_from_slice(&read_scalar(encoded_scalar)?.b); } // Return infinity point if all points are infinity diff --git a/crates/precompile/src/bls12_381/map_fp2_to_g2.rs b/crates/precompile/src/bls12_381/map_fp2_to_g2.rs index 31bf3a8172..fcd7c902a1 100644 --- a/crates/precompile/src/bls12_381/map_fp2_to_g2.rs +++ b/crates/precompile/src/bls12_381/map_fp2_to_g2.rs @@ -1,5 +1,5 @@ use super::{ - blst::{check_canonical_fp2, encode_g2_point, map_fp2_to_g2 as blst_map_fp2_to_g2}, + blst::{encode_g2_point, map_fp2_to_g2 as blst_map_fp2_to_g2, read_fp2}, utils::remove_fp_padding, }; use crate::bls12_381_const::{ @@ -31,7 +31,7 @@ pub(super) fn map_fp2_to_g2(input: &Bytes, gas_limit: u64) -> PrecompileResult { let input_p0_x = remove_fp_padding(&input[..PADDED_FP_LENGTH])?; let input_p0_y = remove_fp_padding(&input[PADDED_FP_LENGTH..PADDED_FP2_LENGTH])?; - let fp2 = check_canonical_fp2(input_p0_x, input_p0_y)?; + let fp2 = read_fp2(input_p0_x, input_p0_y)?; let p_aff = blst_map_fp2_to_g2(&fp2); let out = encode_g2_point(&p_aff); diff --git a/crates/precompile/src/bls12_381/map_fp_to_g1.rs b/crates/precompile/src/bls12_381/map_fp_to_g1.rs index 85d117abdd..b9dc0fd537 100644 --- a/crates/precompile/src/bls12_381/map_fp_to_g1.rs +++ b/crates/precompile/src/bls12_381/map_fp_to_g1.rs @@ -1,5 +1,5 @@ use super::{ - blst::{encode_g1_point, fp_from_bendian, map_fp_to_g1 as blst_map_fp_to_g1}, + blst::{encode_g1_point, map_fp_to_g1 as blst_map_fp_to_g1, read_fp}, utils::remove_fp_padding, }; use crate::bls12_381_const::{MAP_FP_TO_G1_ADDRESS, MAP_FP_TO_G1_BASE_GAS_FEE, PADDED_FP_LENGTH}; @@ -26,7 +26,7 @@ pub(super) fn map_fp_to_g1(input: &Bytes, gas_limit: u64) -> PrecompileResult { } let input_p0 = remove_fp_padding(input)?; - let fp = fp_from_bendian(input_p0)?; + let fp = read_fp(input_p0)?; let p_aff = blst_map_fp_to_g1(&fp); let out = encode_g1_point(&p_aff); diff --git a/crates/precompile/src/bls12_381/pairing.rs b/crates/precompile/src/bls12_381/pairing.rs index 64686dbcca..04a8a3dd96 100644 --- a/crates/precompile/src/bls12_381/pairing.rs +++ b/crates/precompile/src/bls12_381/pairing.rs @@ -1,4 +1,4 @@ -use super::blst::{extract_g1_input, extract_g2_input, pairing_check}; +use super::blst::{pairing_check, read_g1, read_g2}; use super::utils::{remove_g1_padding, remove_g2_padding}; use crate::bls12_381_const::{ PADDED_G1_LENGTH, PADDED_G2_LENGTH, PAIRING_ADDRESS, PAIRING_INPUT_LENGTH, @@ -61,8 +61,8 @@ pub(super) fn pairing(input: &Bytes, gas_limit: u64) -> PrecompileResult { // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check. // extract_g1_input and extract_g2_input perform the necessary checks - let p1_aff = extract_g1_input(a_x, a_y)?; - let p2_aff = extract_g2_input(b_x_0, b_x_1, b_y_0, b_y_1)?; + let p1_aff = read_g1(a_x, a_y)?; + let p2_aff = read_g2(b_x_0, b_x_1, b_y_0, b_y_1)?; if !g1_is_zero & !g2_is_zero { pairs.push((p1_aff, p2_aff)); From ac164c83b6fd10e3e1f8cc9a1a798dc51ec4cb35 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Wed, 26 Mar 2025 11:17:07 +0000 Subject: [PATCH 08/13] for type safety and invariant guarantees, use blst_scalar in the API --- crates/precompile/src/bls12_381/blst.rs | 67 +++++++++++++---------- crates/precompile/src/bls12_381/g1_msm.rs | 7 +-- crates/precompile/src/bls12_381/g2_msm.rs | 12 ++-- 3 files changed, 48 insertions(+), 38 deletions(-) diff --git a/crates/precompile/src/bls12_381/blst.rs b/crates/precompile/src/bls12_381/blst.rs index 72957c2f11..c42968d240 100644 --- a/crates/precompile/src/bls12_381/blst.rs +++ b/crates/precompile/src/bls12_381/blst.rs @@ -100,14 +100,21 @@ pub(super) fn p2_add_affine(a: &blst_p2_affine, b: &blst_p2_affine) -> blst_p2_a /// /// Note: The scalar is expected to be in Big Endian format. #[inline] -fn p1_scalar_mul(p: &blst_p1_affine, scalar: &[u8]) -> blst_p1_affine { +fn p1_scalar_mul(p: &blst_p1_affine, scalar: &blst_scalar) -> blst_p1_affine { // Convert point to Jacobian coordinates let p_jacobian = p1_from_affine(p); let mut result = blst_p1::default(); // SAFETY: all inputs are valid blst types - unsafe { blst_p1_mult(&mut result, &p_jacobian, scalar.as_ptr(), scalar.len() * 8) }; + unsafe { + blst_p1_mult( + &mut result, + &p_jacobian, + scalar.b.as_ptr(), + scalar.b.len() * 8, + ) + }; // Convert result back to affine coordinates p1_to_affine(&result) @@ -120,13 +127,20 @@ fn p1_scalar_mul(p: &blst_p1_affine, scalar: &[u8]) -> blst_p1_affine { /// /// Note: The scalar is expected to be in Big Endian format. #[inline] -fn p2_scalar_mul(p: &blst_p2_affine, scalar: &[u8]) -> blst_p2_affine { +fn p2_scalar_mul(p: &blst_p2_affine, scalar: &blst_scalar) -> blst_p2_affine { // Convert point to Jacobian coordinates let p_jacobian = p2_from_affine(p); let mut result = blst_p2::default(); // SAFETY: all inputs are valid blst types - unsafe { blst_p2_mult(&mut result, &p_jacobian, scalar.as_ptr(), scalar.len() * 8) }; + unsafe { + blst_p2_mult( + &mut result, + &p_jacobian, + scalar.b.as_ptr(), + scalar.b.len() * 8, + ) + }; // Convert result back to affine coordinates p2_to_affine(&result) @@ -136,21 +150,16 @@ fn p2_scalar_mul(p: &blst_p2_affine, scalar: &[u8]) -> blst_p2_affine { /// /// Takes a vector of G1 points and corresponding scalars, and returns their weighted sum /// -/// Note: Scalars are expected to be in Big Endian format. -/// This method assumes that `g1_points` does not contain any points at infinity. +/// Note: This method assumes that `g1_points` does not contain any points at infinity. #[inline] pub(super) fn p1_msm( g1_points: Vec, - scalars_bytes: Vec, + scalars: Vec, nbits: usize, ) -> blst_p1_affine { - assert!( - scalars_bytes.len() % SCALAR_LENGTH == 0, - "Each scalar should be {SCALAR_LENGTH} bytes" - ); assert_eq!( g1_points.len(), - scalars_bytes.len() / SCALAR_LENGTH, + scalars.len(), "number of scalars should equal the number of g1 points" ); @@ -167,9 +176,10 @@ pub(super) fn p1_msm( // When there is only a single point, we use a simpler scalar multiplication // procedure if g1_points.len() == 1 { - return p1_scalar_mul(&g1_points[0], &scalars_bytes); + return p1_scalar_mul(&g1_points[0], &scalars[0]); } + let scalars_bytes: Vec<_> = scalars.into_iter().flat_map(|s| s.b).collect(); // Perform multi-scalar multiplication let multiexp = g1_points.mult(&scalars_bytes, nbits); @@ -186,17 +196,12 @@ pub(super) fn p1_msm( #[inline] pub(super) fn p2_msm( g2_points: Vec, - scalars_bytes: Vec, + scalars: Vec, nbits: usize, ) -> blst_p2_affine { - assert!( - scalars_bytes.len() % SCALAR_LENGTH == 0, - "Each scalar should be {SCALAR_LENGTH} bytes" - ); - assert_eq!( g2_points.len(), - scalars_bytes.len() / SCALAR_LENGTH, + scalars.len(), "number of scalars should equal the number of g2 points" ); @@ -213,9 +218,11 @@ pub(super) fn p2_msm( // When there is only a single point, we use a simpler scalar multiplication // procedure if g2_points.len() == 1 { - return p2_scalar_mul(&g2_points[0], &scalars_bytes); + return p2_scalar_mul(&g2_points[0], &scalars[0]); } + let scalars_bytes: Vec<_> = scalars.into_iter().flat_map(|s| s.b).collect(); + // Perform multi-scalar multiplication let multiexp = g2_points.mult(&scalars_bytes, nbits); @@ -330,7 +337,7 @@ pub(super) fn pairing_check(pairs: &[(blst_p1_affine, blst_p2_affine)]) -> bool } /// Encodes a G1 point in affine format into byte slice with padded elements. -/// +/// /// Note: The encoded bytes are in Big Endian format. pub(super) fn encode_g1_point(input: *const blst_p1_affine) -> [u8; PADDED_G1_LENGTH] { let mut out = [0u8; PADDED_G1_LENGTH]; @@ -343,7 +350,7 @@ pub(super) fn encode_g1_point(input: *const blst_p1_affine) -> [u8; PADDED_G1_LE } /// Encodes a single finite field element into byte slice with padding. -/// +/// /// Note: The encoded bytes are in Big Endian format. fn fp_to_bytes(out: &mut [u8], input: *const blst_fp) { if out.len() != PADDED_FP_LENGTH { @@ -359,7 +366,7 @@ fn fp_to_bytes(out: &mut [u8], input: *const blst_fp) { /// affine coordinates of the point. /// /// Note: Coordinates are expected to be in Big Endian format. -/// +/// /// - If the x or y coordinate do not represent a canonical field element, an error is returned. /// See [read_fp] for more information. /// - If the point is not on the curve, an error is returned. @@ -444,7 +451,7 @@ fn _extract_g1_input( } /// Encodes a G2 point in affine format into byte slice with padded elements. -/// +/// /// Note: The encoded bytes are in Big Endian format. pub(super) fn encode_g2_point(input: &blst_p2_affine) -> [u8; PADDED_G2_LENGTH] { let mut out = [0u8; PADDED_G2_LENGTH]; @@ -468,7 +475,7 @@ pub(super) fn encode_g2_point(input: &blst_p2_affine) -> [u8; PADDED_G2_LENGTH] /// affine coordinates of the point. /// /// Note: Coordinates are expected to be in Big Endian format. -/// +/// /// - If the x or y coordinate do not represent a canonical field element, an error is returned. /// See [read_fp2] for more information. /// - If the point is not on the curve, an error is returned. @@ -500,7 +507,7 @@ fn decode_g2_on_curve( } /// Creates a blst_fp2 element from two field elements. -/// +/// /// Field elements are expected to be in Big Endian format. /// Returns an error if either of the input field elements is not canonical. pub(super) fn read_fp2( @@ -516,7 +523,8 @@ pub(super) fn read_fp2( } /// Extracts a G2 point in Affine format from the x and y coordinates. /// -/// Note: By default, subgroup checks are performed. +/// Note: Coordinates are expected to be in Big Endian format. +/// By default, subgroup checks are performed. pub(super) fn read_g2( a_x_0: &[u8; FP_LENGTH], a_x_1: &[u8; FP_LENGTH], @@ -528,7 +536,8 @@ pub(super) fn read_g2( /// Extracts a G2 point in Affine format from the x and y coordinates /// without performing a subgroup check. /// -/// Note: Skipping subgroup checks can introduce security issues. +/// Note: Coordinates are expected to be in Big Endian format. +/// Skipping subgroup checks can introduce security issues. /// This method should only be called if: /// - The EIP specifies that no subgroup check should be performed /// - One can be certain that the point is in the correct subgroup. diff --git a/crates/precompile/src/bls12_381/g1_msm.rs b/crates/precompile/src/bls12_381/g1_msm.rs index b74ca81c2b..4565d5f98a 100644 --- a/crates/precompile/src/bls12_381/g1_msm.rs +++ b/crates/precompile/src/bls12_381/g1_msm.rs @@ -35,7 +35,7 @@ pub(super) fn g1_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { } let mut g1_points: Vec<_> = Vec::with_capacity(k); - let mut scalars_bytes: Vec = Vec::with_capacity(k * SCALAR_LENGTH); + let mut scalars: Vec<_> = Vec::with_capacity(k); for i in 0..k { let encoded_g1_element = &input[i * G1_MSM_INPUT_LENGTH..i * G1_MSM_INPUT_LENGTH + PADDED_G1_LENGTH]; @@ -65,8 +65,7 @@ pub(super) fn g1_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { } g1_points.push(p0_aff); - - scalars_bytes.extend_from_slice(&read_scalar(encoded_scalar)?.b); + scalars.push(read_scalar(encoded_scalar)?); } // Return the encoding for the point at the infinity according to EIP-2537 @@ -79,7 +78,7 @@ pub(super) fn g1_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { )); } - let multiexp_aff = p1_msm(g1_points, scalars_bytes, NBITS); + let multiexp_aff = p1_msm(g1_points, scalars, NBITS); let out = encode_g1_point(&multiexp_aff); Ok(PrecompileOutput::new(required_gas, out.into())) diff --git a/crates/precompile/src/bls12_381/g2_msm.rs b/crates/precompile/src/bls12_381/g2_msm.rs index 3543d66182..bebfa60483 100644 --- a/crates/precompile/src/bls12_381/g2_msm.rs +++ b/crates/precompile/src/bls12_381/g2_msm.rs @@ -35,7 +35,7 @@ pub(super) fn g2_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { } let mut g2_points: Vec<_> = Vec::with_capacity(k); - let mut scalars_bytes: Vec = Vec::with_capacity(k * SCALAR_LENGTH); + let mut scalars: Vec<_> = Vec::with_capacity(k); for i in 0..k { let encoded_g2_element = &input[i * G2_MSM_INPUT_LENGTH..i * G2_MSM_INPUT_LENGTH + PADDED_G2_LENGTH]; @@ -67,17 +67,19 @@ pub(super) fn g2_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { // Convert affine point to Jacobian coordinates using our helper function g2_points.push(p0_aff); - - scalars_bytes.extend_from_slice(&read_scalar(encoded_scalar)?.b); + scalars.push(read_scalar(encoded_scalar)?); } // Return infinity point if all points are infinity if g2_points.is_empty() { - return Ok(PrecompileOutput::new(required_gas, [0; 256].into())); + return Ok(PrecompileOutput::new( + required_gas, + [0; PADDED_G2_LENGTH].into(), + )); } // Perform multi-scalar multiplication using the safe wrapper - let multiexp_aff = p2_msm(g2_points, scalars_bytes, NBITS); + let multiexp_aff = p2_msm(g2_points, scalars, NBITS); let out = encode_g2_point(&multiexp_aff); Ok(PrecompileOutput::new(required_gas, out.into())) From a24aa211354792b9ad7290584838df47574949af Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Thu, 27 Mar 2025 10:50:00 +0000 Subject: [PATCH 09/13] move `is_valid_be` into blst --- crates/precompile/src/bls12_381/blst.rs | 9 +++++++-- crates/precompile/src/bls12_381/utils.rs | 7 +------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/precompile/src/bls12_381/blst.rs b/crates/precompile/src/bls12_381/blst.rs index dca76c5481..8d998e2e02 100644 --- a/crates/precompile/src/bls12_381/blst.rs +++ b/crates/precompile/src/bls12_381/blst.rs @@ -1,9 +1,9 @@ // This module contains a safe wrapper around the blst library. use crate::{ - bls12_381::utils::is_valid_be, bls12_381_const::{ - FP_LENGTH, FP_PAD_BY, PADDED_FP_LENGTH, PADDED_G1_LENGTH, PADDED_G2_LENGTH, SCALAR_LENGTH, + FP_LENGTH, FP_PAD_BY, MODULUS_REPR, PADDED_FP_LENGTH, PADDED_G1_LENGTH, PADDED_G2_LENGTH, + SCALAR_LENGTH, }, PrecompileError, }; @@ -627,3 +627,8 @@ pub(super) fn read_scalar(input: &[u8]) -> Result Ok(out) } + +/// Checks if the input is a valid big-endian representation of a field element. +fn is_valid_be(input: &[u8; 48]) -> bool { + *input < MODULUS_REPR +} diff --git a/crates/precompile/src/bls12_381/utils.rs b/crates/precompile/src/bls12_381/utils.rs index b4dbafe13f..e049f09e2a 100644 --- a/crates/precompile/src/bls12_381/utils.rs +++ b/crates/precompile/src/bls12_381/utils.rs @@ -1,5 +1,5 @@ use crate::bls12_381_const::{ - FP_LENGTH, FP_PAD_BY, MODULUS_REPR, PADDED_FP_LENGTH, PADDED_G1_LENGTH, PADDED_G2_LENGTH, + FP_LENGTH, FP_PAD_BY, PADDED_FP_LENGTH, PADDED_G1_LENGTH, PADDED_G2_LENGTH, }; use crate::PrecompileError; @@ -50,8 +50,3 @@ pub(super) fn remove_g2_padding(input: &[u8]) -> Result<[&[u8; 48]; 4], Precompi } Ok(input_fps) } - -/// Checks if the input is a valid big-endian representation of a field element. -pub(super) fn is_valid_be(input: &[u8; 48]) -> bool { - *input < MODULUS_REPR -} From f3c2521baa0f7ff9b09497c6b395701a62b868ef Mon Sep 17 00:00:00 2001 From: kevaundray Date: Thu, 27 Mar 2025 12:34:56 +0000 Subject: [PATCH 10/13] Update crates/precompile/src/bls12_381/utils.rs Co-authored-by: rakita --- crates/precompile/src/bls12_381/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/precompile/src/bls12_381/utils.rs b/crates/precompile/src/bls12_381/utils.rs index e049f09e2a..3eef7bd519 100644 --- a/crates/precompile/src/bls12_381/utils.rs +++ b/crates/precompile/src/bls12_381/utils.rs @@ -36,7 +36,7 @@ pub(super) fn remove_g1_padding(input: &[u8]) -> Result<[&[u8; 48]; 2], Precompi /// remove_g2_padding removes the padding applied to the Fp elements that constitute the /// encoded G2 element. -pub(super) fn remove_g2_padding(input: &[u8]) -> Result<[&[u8; 48]; 4], PrecompileError> { +pub(super) fn remove_g2_padding(input: &[u8]) -> Result<[&[u8; FP_LENGTH]; 4], PrecompileError> { if input.len() != PADDED_G2_LENGTH { return Err(PrecompileError::Other(format!( "Input should be {PADDED_G2_LENGTH} bytes, was {}", From 5215fae07433ee17d583bc914f0d6dc6b5e809d4 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Thu, 27 Mar 2025 12:35:05 +0000 Subject: [PATCH 11/13] Update crates/precompile/src/bls12_381/g1_msm.rs Co-authored-by: rakita --- crates/precompile/src/bls12_381/g1_msm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/precompile/src/bls12_381/g1_msm.rs b/crates/precompile/src/bls12_381/g1_msm.rs index 4565d5f98a..a0d6435878 100644 --- a/crates/precompile/src/bls12_381/g1_msm.rs +++ b/crates/precompile/src/bls12_381/g1_msm.rs @@ -35,7 +35,7 @@ pub(super) fn g1_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { } let mut g1_points: Vec<_> = Vec::with_capacity(k); - let mut scalars: Vec<_> = Vec::with_capacity(k); + let mut scalars = Vec::with_capacity(k); for i in 0..k { let encoded_g1_element = &input[i * G1_MSM_INPUT_LENGTH..i * G1_MSM_INPUT_LENGTH + PADDED_G1_LENGTH]; From 371227681771adb6db6c961c9901c02cc70c0ff1 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Thu, 27 Mar 2025 12:35:18 +0000 Subject: [PATCH 12/13] Update crates/precompile/src/bls12_381/utils.rs Co-authored-by: rakita --- crates/precompile/src/bls12_381/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/precompile/src/bls12_381/utils.rs b/crates/precompile/src/bls12_381/utils.rs index 3eef7bd519..b32e5f302c 100644 --- a/crates/precompile/src/bls12_381/utils.rs +++ b/crates/precompile/src/bls12_381/utils.rs @@ -21,7 +21,7 @@ pub(super) fn remove_fp_padding(input: &[u8]) -> Result<&[u8; FP_LENGTH], Precom } /// remove_g1_padding removes the padding applied to the Fp elements that constitute the /// encoded G1 element. -pub(super) fn remove_g1_padding(input: &[u8]) -> Result<[&[u8; 48]; 2], PrecompileError> { +pub(super) fn remove_g1_padding(input: &[u8]) -> Result<[&[u8; FP_LENGTH]; 2], PrecompileError> { if input.len() != PADDED_G1_LENGTH { return Err(PrecompileError::Other(format!( "Input should be {PADDED_G1_LENGTH} bytes, was {}", From 2695bbd041858f70d68e8bc8334a61a354481be6 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Thu, 27 Mar 2025 12:35:32 +0000 Subject: [PATCH 13/13] Update crates/precompile/src/bls12_381/g2_msm.rs Co-authored-by: rakita --- crates/precompile/src/bls12_381/g2_msm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/precompile/src/bls12_381/g2_msm.rs b/crates/precompile/src/bls12_381/g2_msm.rs index bebfa60483..9bb437ac21 100644 --- a/crates/precompile/src/bls12_381/g2_msm.rs +++ b/crates/precompile/src/bls12_381/g2_msm.rs @@ -35,7 +35,7 @@ pub(super) fn g2_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { } let mut g2_points: Vec<_> = Vec::with_capacity(k); - let mut scalars: Vec<_> = Vec::with_capacity(k); + let mut scalars = Vec::with_capacity(k); for i in 0..k { let encoded_g2_element = &input[i * G2_MSM_INPUT_LENGTH..i * G2_MSM_INPUT_LENGTH + PADDED_G2_LENGTH];