@@ -770,21 +770,23 @@ pub fn ecadd(calldata: &Bytes, gas_remaining: &mut u64, _fork: Fork) -> Result<B
770770pub fn ecmul ( calldata : & Bytes , gas_remaining : & mut u64 , _fork : Fork ) -> Result < Bytes , VMError > {
771771 // If calldata does not reach the required length, we should fill the rest with zeros
772772 let calldata = fill_with_zeros ( calldata, 96 ) ;
773-
774773 increase_precompile_consumed_gas ( ECMUL_COST , gas_remaining) ?;
775774
776- let point_x = calldata . get ( 0 .. 32 ) . ok_or ( InternalError :: Slicing ) ? ;
777- let point_y = calldata . get ( 32 .. 64 ) . ok_or ( InternalError :: Slicing ) ? ;
778- let scalar = calldata . get ( 64 .. 96 ) . ok_or ( InternalError :: Slicing ) ? ;
779-
780- if u256_from_big_endian ( point_x ) >= ALT_BN128_PRIME
781- || u256_from_big_endian ( point_y ) >= ALT_BN128_PRIME
782- {
783- return Err ( PrecompileError :: InvalidPoint . into ( ) ) ;
784- }
775+ let ( Some ( g1 ) , Some ( scalar ) ) = (
776+ parse_bn254_g1 ( & calldata , 0 ) ,
777+ parse_bn254_scalar ( & calldata , 64 ) ,
778+ ) else {
779+ return Err ( InternalError :: Slicing . into ( ) ) ;
780+ } ;
781+ validate_bn254_g1_coords ( & g1 ) ? ;
782+ bn254_g1_mul ( g1 , scalar )
783+ }
785784
786- let x = ark_bn254:: Fq :: from_be_bytes_mod_order ( point_x) ;
787- let y = ark_bn254:: Fq :: from_be_bytes_mod_order ( point_y) ;
785+ #[ cfg( all( not( feature = "sp1" ) , not( feature = "risc0" ) ) ) ]
786+ #[ inline]
787+ pub fn bn254_g1_mul ( point : G1 , scalar : U256 ) -> Result < Bytes , VMError > {
788+ let x = ark_bn254:: Fq :: from_be_bytes_mod_order ( & point. 0 . to_big_endian ( ) ) ;
789+ let y = ark_bn254:: Fq :: from_be_bytes_mod_order ( & point. 1 . to_big_endian ( ) ) ;
788790
789791 if x. is_zero ( ) && y. is_zero ( ) {
790792 return Ok ( Bytes :: from ( [ 0u8 ; 64 ] . to_vec ( ) ) ) ;
@@ -795,7 +797,7 @@ pub fn ecmul(calldata: &Bytes, gas_remaining: &mut u64, _fork: Fork) -> Result<B
795797 return Err ( PrecompileError :: InvalidPoint . into ( ) ) ;
796798 }
797799
798- let scalar = FrArk :: from_be_bytes_mod_order ( scalar) ;
800+ let scalar = FrArk :: from_be_bytes_mod_order ( & scalar. to_big_endian ( ) ) ;
799801 if scalar. is_zero ( ) {
800802 return Ok ( Bytes :: from ( [ 0u8 ; 64 ] . to_vec ( ) ) ) ;
801803 }
@@ -811,6 +813,41 @@ pub fn ecmul(calldata: &Bytes, gas_remaining: &mut u64, _fork: Fork) -> Result<B
811813 Ok ( Bytes :: from ( out) )
812814}
813815
816+ #[ cfg( any( feature = "sp1" , feature = "risc0" ) ) ]
817+ #[ inline]
818+ pub fn bn254_g1_mul ( g1 : G1 , scalar : U256 ) -> Result < Bytes , VMError > {
819+ use substrate_bn:: { AffineG1 , Fq , Fr , G1 , Group } ;
820+
821+ if g1. is_zero ( ) || scalar. is_zero ( ) {
822+ return Ok ( Bytes :: from ( [ 0u8 ; 64 ] . to_vec ( ) ) ) ;
823+ }
824+
825+ let ( g1_x, g1_y) = (
826+ Fq :: from_slice ( & g1. 0 . to_big_endian ( ) ) . map_err ( |_| PrecompileError :: ParsingInputError ) ?,
827+ Fq :: from_slice ( & g1. 1 . to_big_endian ( ) ) . map_err ( |_| PrecompileError :: ParsingInputError ) ?,
828+ ) ;
829+
830+ let g1 = AffineG1 :: new ( g1_x, g1_y) . map_err ( |_| PrecompileError :: InvalidPoint ) ?;
831+
832+ let scalar =
833+ Fr :: from_slice ( & scalar. to_big_endian ( ) ) . map_err ( |_| PrecompileError :: ParsingInputError ) ?;
834+
835+ #[ cfg( feature = "risc0" ) ]
836+ // RISC0's substrate-bn patch does not implement Mul<Fr> for AffineG1, but
837+ // it does implement Mul<Fr> for G1. So we convert AffineG1 to G1 first.
838+ let result = G1 :: from ( g1) * scalar;
839+ #[ cfg( feature = "sp1" ) ]
840+ let result = g1 * scalar;
841+
842+ let mut x_bytes = [ 0u8 ; 32 ] ;
843+ let mut y_bytes = [ 0u8 ; 32 ] ;
844+ result. x ( ) . to_big_endian ( & mut x_bytes) ;
845+ result. y ( ) . to_big_endian ( & mut y_bytes) ;
846+ let out = [ x_bytes, y_bytes] . concat ( ) ;
847+
848+ Ok ( Bytes :: from ( out) )
849+ }
850+
814851const ALT_BN128_PRIME : U256 = U256 ( [
815852 0x3c208c16d87cfd47 ,
816853 0x97816a916871ca8d ,
@@ -833,38 +870,58 @@ impl G2 {
833870 }
834871}
835872
873+ /// Parses 32 bytes as BN254 scalar
836874#[ inline]
837- fn parse_bn254_coords ( buf : & [ u8 ; 192 ] ) -> ( G1 , G2 ) {
838- let ( g1_x, g1_y) = (
839- u256_from_big_endian ( & buf[ ..32 ] ) ,
840- u256_from_big_endian ( & buf[ 32 ..64 ] ) ,
841- ) ;
842- let ( g2_xy, g2_xx, g2_yy, g2_yx) = (
843- u256_from_big_endian ( & buf[ 64 ..96 ] ) ,
844- u256_from_big_endian ( & buf[ 96 ..128 ] ) ,
845- u256_from_big_endian ( & buf[ 128 ..160 ] ) ,
846- u256_from_big_endian ( & buf[ 160 ..] ) ,
847- ) ;
875+ fn parse_bn254_scalar ( buf : & [ u8 ] , offset : usize ) -> Option < U256 > {
876+ buf. get ( offset..offset. checked_add ( 32 ) ?)
877+ . map ( u256_from_big_endian)
878+ }
879+
880+ /// Parses 64 bytes as a BN254 G1 point
881+ #[ inline]
882+ fn parse_bn254_g1 ( buf : & [ u8 ] , offset : usize ) -> Option < G1 > {
883+ let chunk = buf. get ( offset..offset. checked_add ( 64 ) ?) ?;
884+ let ( x_bytes, y_bytes) = chunk. split_at_checked ( 32 ) ?;
885+ Some ( G1 (
886+ u256_from_big_endian ( x_bytes) ,
887+ u256_from_big_endian ( y_bytes) ,
888+ ) )
889+ }
848890
849- ( G1 ( g1_x, g1_y) , G2 ( g2_xx, g2_xy, g2_yx, g2_yy) )
891+ /// Parses 128 bytes as a BN254 G2 point
892+ fn parse_bn254_g2 ( buf : & [ u8 ] , offset : usize ) -> Option < G2 > {
893+ let chunk = buf. get ( offset..offset. checked_add ( 128 ) ?) ?;
894+ let ( g2_xy, rest) = chunk. split_at_checked ( 32 ) ?;
895+ let ( g2_xx, rest) = rest. split_at_checked ( 32 ) ?;
896+ let ( g2_yy, g2_yx) = rest. split_at_checked ( 32 ) ?;
897+ Some ( G2 (
898+ u256_from_big_endian ( g2_xx) ,
899+ u256_from_big_endian ( g2_xy) ,
900+ u256_from_big_endian ( g2_yx) ,
901+ u256_from_big_endian ( g2_yy) ,
902+ ) )
850903}
851904
852905#[ inline]
853- fn validate_bn254_coords ( g1 : & G1 , g2 : & G2 ) -> Result < bool , VMError > {
906+ fn validate_bn254_g1_coords ( g1 : & G1 ) -> Result < ( ) , VMError > {
854907 // check each element is in field
855908 if g1. 0 >= ALT_BN128_PRIME || g1. 1 >= ALT_BN128_PRIME {
856909 return Err ( PrecompileError :: CoordinateExceedsFieldModulus . into ( ) ) ;
857910 }
911+ Ok ( ( ) )
912+ }
858913
914+ #[ inline]
915+ fn validate_bn254_g2_coords ( g2 : & G2 ) -> Result < ( ) , VMError > {
916+ // check each element is in field
859917 if g2. 0 >= ALT_BN128_PRIME
860918 || g2. 1 >= ALT_BN128_PRIME
861919 || g2. 2 >= ALT_BN128_PRIME
862920 || g2. 3 >= ALT_BN128_PRIME
863921 {
864922 return Err ( PrecompileError :: CoordinateExceedsFieldModulus . into ( ) ) ;
865923 }
866-
867- Ok ( true )
924+ Ok ( ( ) )
868925}
869926
870927/// Performs a bilinear pairing on points on the elliptic curve 'alt_bn128', returns 1 on success and 0 on failure
@@ -880,12 +937,12 @@ pub fn ecpairing(calldata: &Bytes, gas_remaining: &mut u64, _fork: Fork) -> Resu
880937
881938 let mut batch = Vec :: new ( ) ;
882939 for input in calldata. chunks_exact ( 192 ) {
883- # [ expect ( unsafe_code , reason = "chunks_exact ensures the conversion is valid" ) ]
884- let input : [ u8 ; 192 ] = unsafe { input . try_into ( ) . unwrap_unchecked ( ) } ;
885- let ( g1 , g2 ) = parse_bn254_coords ( & input ) ;
886- if validate_bn254_coords ( & g1, & g2 ) ? {
887- batch . push ( ( g1 , g2) ) ;
888- }
940+ let ( Some ( g1 ) , Some ( g2 ) ) = ( parse_bn254_g1 ( input , 0 ) , parse_bn254_g2 ( input , 64 ) ) else {
941+ return Err ( InternalError :: Slicing . into ( ) ) ;
942+ } ;
943+ validate_bn254_g1_coords ( & g1) ? ;
944+ validate_bn254_g2_coords ( & g2) ? ;
945+ batch . push ( ( g1 , g2 ) ) ;
889946 }
890947
891948 let pairing_check = if batch. is_empty ( ) {
0 commit comments