22use crate::xdr::ScVal;
33use crate::{
44 env::internal::{self, BytesObject, U256Val, U64Val},
5- impl_bytesn_repr ,
5+ impl_bytesn_repr_without_from_bytes ,
66 unwrap::{UnwrapInfallible, UnwrapOptimized},
77 Bytes, BytesN, ConversionError, Env, IntoVal, TryFromVal, Val, Vec, U256,
88};
@@ -162,10 +162,55 @@ pub struct Fp2(BytesN<FP2_SERIALIZED_SIZE>);
162162#[repr(transparent)]
163163pub struct Fr(U256);
164164
165- impl_bytesn_repr!(G1Affine, G1_SERIALIZED_SIZE);
166- impl_bytesn_repr!(G2Affine, G2_SERIALIZED_SIZE);
167- impl_bytesn_repr!(Fp, FP_SERIALIZED_SIZE);
168- impl_bytesn_repr!(Fp2, FP2_SERIALIZED_SIZE);
165+ impl_bytesn_repr_without_from_bytes!(G1Affine, G1_SERIALIZED_SIZE);
166+ impl_bytesn_repr_without_from_bytes!(G2Affine, G2_SERIALIZED_SIZE);
167+ impl_bytesn_repr_without_from_bytes!(Fp, FP_SERIALIZED_SIZE);
168+ impl_bytesn_repr_without_from_bytes!(Fp2, FP2_SERIALIZED_SIZE);
169+
170+ // BLS12-381 base field modulus p in big-endian bytes.
171+ // p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
172+ const BLS12_381_FP_MODULUS_BE: [u8; FP_SERIALIZED_SIZE] = [
173+ 0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, 0xd7,
174+ 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, 0xf6, 0xb0, 0xf6, 0x24,
175+ 0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xab,
176+ ];
177+
178+ fn validate_fp(bytes: &[u8; FP_SERIALIZED_SIZE]) {
179+ if bytes >= &BLS12_381_FP_MODULUS_BE {
180+ sdk_panic!("Bls12-381: Invalid Fp");
181+ }
182+ }
183+
184+ fn validate_fp2(bytes: &[u8; FP2_SERIALIZED_SIZE]) {
185+ validate_fp(bytes[0..FP_SERIALIZED_SIZE].try_into().unwrap());
186+ validate_fp(bytes[FP_SERIALIZED_SIZE..].try_into().unwrap());
187+ }
188+
189+ impl G1Affine {
190+ pub fn from_bytes(bytes: BytesN<G1_SERIALIZED_SIZE>) -> Self {
191+ Self(bytes)
192+ }
193+ }
194+
195+ impl G2Affine {
196+ pub fn from_bytes(bytes: BytesN<G2_SERIALIZED_SIZE>) -> Self {
197+ Self(bytes)
198+ }
199+ }
200+
201+ impl Fp {
202+ pub fn from_bytes(bytes: BytesN<FP_SERIALIZED_SIZE>) -> Self {
203+ validate_fp(&bytes.to_array());
204+ Self(bytes)
205+ }
206+ }
207+
208+ impl Fp2 {
209+ pub fn from_bytes(bytes: BytesN<FP2_SERIALIZED_SIZE>) -> Self {
210+ validate_fp2(&bytes.to_array());
211+ Self(bytes)
212+ }
213+ }
169214
170215impl Fp {
171216 pub fn env(&self) -> &Env {
@@ -476,9 +521,28 @@ impl Fr {
476521 }
477522}
478523
524+ // BLS12-381 scalar field modulus r in big-endian bytes.
525+ // r = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
526+ const BLS12_381_FR_MODULUS_BE: [u8; 32] = [
527+ 0x73, 0xed, 0xa7, 0x53, 0x29, 0x9d, 0x7d, 0x48, 0x33, 0x39, 0xd8, 0x08, 0x09, 0xa1, 0xd8, 0x05,
528+ 0x53, 0xbd, 0xa4, 0x02, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
529+ ];
530+
531+ fn fr_modulus(env: &Env) -> U256 {
532+ U256::from_be_bytes(env, &Bytes::from_array(env, &BLS12_381_FR_MODULUS_BE))
533+ }
534+
479535impl From<U256> for Fr {
480536 fn from(value: U256) -> Self {
481- Self(value)
537+ // Keep all Fr construction paths canonical by reducing modulo r here.
538+ // Skip the expensive rem_euclid when value is already canonical (< r),
539+ // which is always the case for host-returned arithmetic results.
540+ let modulus = fr_modulus(value.env());
541+ if value >= modulus {
542+ Self(value.rem_euclid(&modulus))
543+ } else {
544+ Self(value)
545+ }
482546 }
483547}
484548
@@ -493,7 +557,7 @@ impl TryFromVal<Env, Val> for Fr {
493557
494558 fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
495559 let u = U256::try_from_val(env, val)?;
496- Ok(Fr(u ))
560+ Ok(u.into( ))
497561 }
498562}
499563
@@ -773,6 +837,7 @@ impl Bls12_381 {
773837#[cfg(test)]
774838mod test {
775839 use super::*;
840+ use crate::bytesn;
776841
777842 #[test]
778843 fn test_g1affine_to_val() {
@@ -839,4 +904,173 @@ mod test {
839904
840905 assert_eq!(fr, rt);
841906 }
907+
908+ #[test]
909+ fn test_fr_eq_both_unreduced() {
910+ let env = Env::default();
911+ let r = fr_modulus(&env);
912+ let one = U256::from_u32(&env, 1);
913+
914+ let a = Fr::from_u256(r.add(&one));
915+ let b = Fr::from_u256(one.clone());
916+ assert_eq!(a, b);
917+
918+ let two_r_plus_one = r.add(&r).add(&one);
919+ let c = Fr::from_u256(two_r_plus_one);
920+ assert_eq!(a, c);
921+ assert_eq!(b, c);
922+ }
923+
924+ #[test]
925+ fn test_fr_eq_unreduced_vs_zero() {
926+ let env = Env::default();
927+ let r = fr_modulus(&env);
928+ let zero = U256::from_u32(&env, 0);
929+
930+ let a = Fr::from_u256(r);
931+ let b = Fr::from_u256(zero);
932+ assert_eq!(a, b);
933+ }
934+
935+ #[test]
936+ fn test_fr_reduced_value_unchanged() {
937+ let env = Env::default();
938+ let r = fr_modulus(&env);
939+ let val = r.sub(&U256::from_u32(&env, 1));
940+
941+ let fr = Fr::from_u256(val.clone());
942+ assert_eq!(fr.to_u256(), val);
943+
944+ let fr42 = Fr::from_u256(U256::from_u32(&env, 42));
945+ assert_eq!(fr42.to_u256(), U256::from_u32(&env, 42));
946+ }
947+
948+ #[test]
949+ fn test_fr_from_bytes_reduces() {
950+ let env = Env::default();
951+ let one_fr = Fr::from_u256(U256::from_u32(&env, 1));
952+
953+ // BLS12-381 r+1 as big-endian bytes
954+ let fr_from_bytes = Fr::from_bytes(bytesn!(
955+ &env,
956+ 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000002
957+ ));
958+ assert_eq!(fr_from_bytes, one_fr);
959+ }
960+
961+ #[test]
962+ fn test_fr_try_from_val_reduces() {
963+ let env = Env::default();
964+ let r = fr_modulus(&env);
965+ let one = U256::from_u32(&env, 1);
966+
967+ let unreduced_u256 = r.add(&one);
968+ let val: Val = unreduced_u256.into_val(&env);
969+ let fr_from_val: Fr = val.into_val(&env);
970+ let fr_one = Fr::from_u256(one);
971+ assert_eq!(fr_from_val, fr_one);
972+ }
973+
974+ #[test]
975+ fn test_fr_u256_into_reduces() {
976+ let env = Env::default();
977+ let r = fr_modulus(&env);
978+ let one = U256::from_u32(&env, 1);
979+
980+ let fr: Fr = r.add(&one).into();
981+ let fr_one: Fr = one.into();
982+ assert_eq!(fr, fr_one);
983+ }
984+
985+ #[test]
986+ fn test_fr_eq_unreduced_vs_host_computed() {
987+ let env = Env::default();
988+ let bls = Bls12_381::new(&env);
989+ let r = fr_modulus(&env);
990+ let five = U256::from_u32(&env, 5);
991+
992+ let user_fr = Fr::from_u256(r.add(&five));
993+ let host_fr = bls.fr_add(
994+ &Fr::from_u256(U256::from_u32(&env, 2)),
995+ &Fr::from_u256(U256::from_u32(&env, 3)),
996+ );
997+ assert_eq!(user_fr, host_fr);
998+ }
999+
1000+ // Fp validation tests
1001+
1002+ #[test]
1003+ fn test_fp_max_valid_accepted() {
1004+ let env = Env::default();
1005+ let mut p_minus_1 = BLS12_381_FP_MODULUS_BE;
1006+ p_minus_1[FP_SERIALIZED_SIZE - 1] -= 1;
1007+ let _ = Fp::from_array(&env, &p_minus_1);
1008+ }
1009+
1010+ #[test]
1011+ #[should_panic(expected = "Bls12-381: Invalid Fp")]
1012+ fn test_fp_at_modulus_panics() {
1013+ let env = Env::default();
1014+ let _ = Fp::from_array(&env, &BLS12_381_FP_MODULUS_BE);
1015+ }
1016+
1017+ #[test]
1018+ #[should_panic(expected = "Bls12-381: Invalid Fp")]
1019+ fn test_fp_above_modulus_panics() {
1020+ let env = Env::default();
1021+ let mut above = BLS12_381_FP_MODULUS_BE;
1022+ above[FP_SERIALIZED_SIZE - 1] += 1;
1023+ let _ = Fp::from_array(&env, &above);
1024+ }
1025+
1026+ #[test]
1027+ fn test_fp_from_bytes_validates() {
1028+ let env = Env::default();
1029+ let _ = Fp::from_bytes(BytesN::from_array(&env, &[0u8; FP_SERIALIZED_SIZE]));
1030+ }
1031+
1032+ #[test]
1033+ #[should_panic(expected = "Bls12-381: Invalid Fp")]
1034+ fn test_fp_from_bytes_rejects_modulus() {
1035+ let env = Env::default();
1036+ let _ = Fp::from_bytes(BytesN::from_array(&env, &BLS12_381_FP_MODULUS_BE));
1037+ }
1038+
1039+ #[test]
1040+ #[should_panic(expected = "Bls12-381: Invalid Fp")]
1041+ fn test_fp_try_from_val_rejects_modulus() {
1042+ let env = Env::default();
1043+ let bytes = BytesN::from_array(&env, &BLS12_381_FP_MODULUS_BE);
1044+ let val: Val = bytes.into_val(&env);
1045+ let _: Fp = val.into_val(&env);
1046+ }
1047+
1048+ #[test]
1049+ #[should_panic(expected = "Bls12-381: Invalid Fp")]
1050+ fn test_fp2_component_above_modulus_panics() {
1051+ let env = Env::default();
1052+ let mut fp2_bytes = [0u8; FP2_SERIALIZED_SIZE];
1053+ fp2_bytes[0..FP_SERIALIZED_SIZE].copy_from_slice(&BLS12_381_FP_MODULUS_BE);
1054+ let _ = Fp2::from_array(&env, &fp2_bytes);
1055+ }
1056+
1057+ #[test]
1058+ #[should_panic(expected = "Bls12-381: Invalid Fp")]
1059+ fn test_fp2_second_component_above_modulus_panics() {
1060+ let env = Env::default();
1061+ let mut fp2_bytes = [0u8; FP2_SERIALIZED_SIZE];
1062+ fp2_bytes[FP_SERIALIZED_SIZE..].copy_from_slice(&BLS12_381_FP_MODULUS_BE);
1063+ let _ = Fp2::from_array(&env, &fp2_bytes);
1064+ }
1065+
1066+ #[test]
1067+ fn test_fp2_max_valid_accepted() {
1068+ let env = Env::default();
1069+ let mut p_minus_1 = BLS12_381_FP_MODULUS_BE;
1070+ p_minus_1[FP_SERIALIZED_SIZE - 1] -= 1;
1071+ let mut fp2_bytes = [0u8; FP2_SERIALIZED_SIZE];
1072+ fp2_bytes[0..FP_SERIALIZED_SIZE].copy_from_slice(&p_minus_1);
1073+ fp2_bytes[FP_SERIALIZED_SIZE..].copy_from_slice(&p_minus_1);
1074+ let _ = Fp2::from_array(&env, &fp2_bytes);
1075+ }
8421076}
0 commit comments