33//! We roughly follow pornin/ecgfp5.
44use core:: ops:: { Add , Mul } ;
55use std:: {
6- array,
6+ array, fmt ,
77 ops:: { AddAssign , Neg , Sub } ,
88 sync:: LazyLock ,
99} ;
@@ -23,7 +23,7 @@ use plonky2::{
2323 util:: serialization:: { Read , Write } ,
2424} ;
2525use rand:: rngs:: OsRng ;
26- use serde:: { Deserialize , Serialize } ;
26+ use serde:: { Deserialize , Deserializer , Serialize , Serializer } ;
2727
2828use crate :: backends:: plonky2:: {
2929 circuits:: common:: ValueTarget ,
@@ -94,12 +94,65 @@ fn ec_field_from_bytes(b: &[u8]) -> Result<ECField, Error> {
9494 Ok ( QuinticExtension ( array:: from_fn ( |i| fields[ i] ) ) )
9595}
9696
97- #[ derive( Clone , Copy , Debug , PartialEq , Eq , Serialize , Deserialize ) ]
97+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
9898pub struct Point {
9999 pub x : ECField ,
100100 pub u : ECField ,
101101}
102102
103+ impl fmt:: Display for Point {
104+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
105+ #[ allow( clippy:: collapsible_else_if) ]
106+ if f. alternate ( ) {
107+ write ! ( f, "({}, {})" , self . x, self . u)
108+ } else {
109+ if self . is_in_subgroup ( ) {
110+ // Compressed
111+ let u_bytes = self . as_bytes_from_subgroup ( ) . expect ( "point in subgroup" ) ;
112+ let u_b58 = bs58:: encode ( u_bytes) . into_string ( ) ;
113+ write ! ( f, "{}" , u_b58)
114+ } else {
115+ // Non-compressed
116+ let xu_bytes = [ ec_field_to_bytes ( & self . x ) , ec_field_to_bytes ( & self . u ) ] . concat ( ) ;
117+ let xu_b58 = bs58:: encode ( xu_bytes) . into_string ( ) ;
118+ write ! ( f, "{}" , xu_b58)
119+ }
120+ }
121+ }
122+ }
123+
124+ impl Serialize for Point {
125+ fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
126+ where
127+ S : Serializer ,
128+ {
129+ let point_b58 = format ! ( "{}" , self ) ;
130+ serializer. serialize_str ( & point_b58)
131+ }
132+ }
133+
134+ impl < ' de > Deserialize < ' de > for Point {
135+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
136+ where
137+ D : Deserializer < ' de > ,
138+ {
139+ let point_b58 = String :: deserialize ( deserializer) ?;
140+ let point_bytes: Vec < u8 > = bs58:: decode ( point_b58)
141+ . into_vec ( )
142+ . map_err ( serde:: de:: Error :: custom) ?;
143+ if point_bytes. len ( ) == 80 {
144+ // Non-compressed
145+ Ok ( Point {
146+ x : ec_field_from_bytes ( & point_bytes[ ..40 ] ) . map_err ( serde:: de:: Error :: custom) ?,
147+ u : ec_field_from_bytes ( & point_bytes[ 40 ..] ) . map_err ( serde:: de:: Error :: custom) ?,
148+ } )
149+ } else {
150+ // Compressed
151+ Self :: from_bytes_into_subgroup ( & point_bytes) . map_err ( serde:: de:: Error :: custom)
152+ }
153+ }
154+ }
155+
103156impl Point {
104157 pub fn new_rand_from_subgroup ( ) -> Self {
105158 & OsRng . gen_biguint_below ( & GROUP_ORDER ) * Self :: generator ( )
@@ -111,8 +164,8 @@ impl Point {
111164 match self . is_in_subgroup ( ) {
112165 true => Ok ( self . u ) ,
113166 false => Err ( Error :: custom ( format ! (
114- "Point must lie in EC subgroup: ({}, {}) " ,
115- self . x , self . u
167+ "Point must lie in EC subgroup: {} " ,
168+ self
116169 ) ) ) ,
117170 }
118171 }
@@ -810,7 +863,7 @@ mod test {
810863 match p == q {
811864 true => Ok ( ( ) ) ,
812865 false => Err ( Error :: custom ( format ! (
813- "Roundtrip compression failed: {:? } ≠ {:? }" ,
866+ "Roundtrip compression failed: {} ≠ {}" ,
814867 p, q
815868 ) ) ) ,
816869 }
@@ -897,4 +950,28 @@ mod test {
897950 assert ! ( data. prove( pw) . is_err( ) ) ;
898951 Ok ( ( ) )
899952 }
953+
954+ #[ test]
955+ fn test_point_serialize_deserialize ( ) -> Result < ( ) , anyhow:: Error > {
956+ // In subgroup
957+ let g = Point :: generator ( ) ;
958+
959+ let serialized = serde_json:: to_string_pretty ( & g) ?;
960+ println ! ( "g = {}" , serialized) ;
961+ let deserialized = serde_json:: from_str ( & serialized) ?;
962+ assert_eq ! ( g, deserialized) ;
963+
964+ // Not in subgroup
965+ let not_sub = Point {
966+ x : Point :: b ( ) / g. x ,
967+ u : g. u ,
968+ } ;
969+
970+ let serialized = serde_json:: to_string_pretty ( & not_sub) ?;
971+ println ! ( "not_sub = {}" , serialized) ;
972+ let deserialized = serde_json:: from_str ( & serialized) ?;
973+ assert_eq ! ( not_sub, deserialized) ;
974+
975+ Ok ( ( ) )
976+ }
900977}
0 commit comments