1
1
//! Optional pubkeys that can be used a `Pod`s
2
2
#[ cfg( feature = "borsh" ) ]
3
3
use borsh:: { BorshDeserialize , BorshSchema , BorshSerialize } ;
4
+ use {
5
+ bytemuck_derive:: { Pod , Zeroable } ,
6
+ solana_program:: { program_error:: ProgramError , program_option:: COption , pubkey:: Pubkey } ,
7
+ solana_zk_sdk:: encryption:: pod:: elgamal:: PodElGamalPubkey ,
8
+ } ;
4
9
#[ cfg( feature = "serde-traits" ) ]
5
10
use {
6
- base64:: { prelude:: BASE64_STANDARD , Engine } ,
7
11
serde:: de:: { Error , Unexpected , Visitor } ,
8
12
serde:: { Deserialize , Deserializer , Serialize , Serializer } ,
9
13
std:: { convert:: TryFrom , fmt, str:: FromStr } ,
10
14
} ;
11
- use {
12
- bytemuck_derive:: { Pod , Zeroable } ,
13
- solana_program:: { program_error:: ProgramError , program_option:: COption , pubkey:: Pubkey } ,
14
- solana_zk_token_sdk:: zk_token_elgamal:: pod:: ElGamalPubkey ,
15
- } ;
16
15
17
16
/// A Pubkey that encodes `None` as all `0`, meant to be usable as a Pod type,
18
17
/// similar to all NonZero* number types from the bytemuck library.
@@ -131,21 +130,21 @@ impl<'de> Deserialize<'de> for OptionalNonZeroPubkey {
131
130
/// type.
132
131
#[ derive( Clone , Copy , Debug , Default , PartialEq , Pod , Zeroable ) ]
133
132
#[ repr( transparent) ]
134
- pub struct OptionalNonZeroElGamalPubkey ( ElGamalPubkey ) ;
133
+ pub struct OptionalNonZeroElGamalPubkey ( PodElGamalPubkey ) ;
135
134
impl OptionalNonZeroElGamalPubkey {
136
135
/// Checks equality between an OptionalNonZeroElGamalPubkey and an
137
136
/// ElGamalPubkey when interpreted as bytes.
138
- pub fn equals ( & self , other : & ElGamalPubkey ) -> bool {
137
+ pub fn equals ( & self , other : & PodElGamalPubkey ) -> bool {
139
138
& self . 0 == other
140
139
}
141
140
}
142
- impl TryFrom < Option < ElGamalPubkey > > for OptionalNonZeroElGamalPubkey {
141
+ impl TryFrom < Option < PodElGamalPubkey > > for OptionalNonZeroElGamalPubkey {
143
142
type Error = ProgramError ;
144
- fn try_from ( p : Option < ElGamalPubkey > ) -> Result < Self , Self :: Error > {
143
+ fn try_from ( p : Option < PodElGamalPubkey > ) -> Result < Self , Self :: Error > {
145
144
match p {
146
- None => Ok ( Self ( ElGamalPubkey :: default ( ) ) ) ,
145
+ None => Ok ( Self ( PodElGamalPubkey :: default ( ) ) ) ,
147
146
Some ( elgamal_pubkey) => {
148
- if elgamal_pubkey == ElGamalPubkey :: default ( ) {
147
+ if elgamal_pubkey == PodElGamalPubkey :: default ( ) {
149
148
Err ( ProgramError :: InvalidArgument )
150
149
} else {
151
150
Ok ( Self ( elgamal_pubkey) )
@@ -154,26 +153,23 @@ impl TryFrom<Option<ElGamalPubkey>> for OptionalNonZeroElGamalPubkey {
154
153
}
155
154
}
156
155
}
157
- impl From < OptionalNonZeroElGamalPubkey > for Option < ElGamalPubkey > {
156
+ impl From < OptionalNonZeroElGamalPubkey > for Option < PodElGamalPubkey > {
158
157
fn from ( p : OptionalNonZeroElGamalPubkey ) -> Self {
159
- if p. 0 == ElGamalPubkey :: default ( ) {
158
+ if p. 0 == PodElGamalPubkey :: default ( ) {
160
159
None
161
160
} else {
162
161
Some ( p. 0 )
163
162
}
164
163
}
165
164
}
166
165
167
- #[ cfg( any( feature = "serde-traits" , test) ) ]
168
- const OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN : usize = 32 ;
169
-
170
166
#[ cfg( feature = "serde-traits" ) ]
171
167
impl Serialize for OptionalNonZeroElGamalPubkey {
172
168
fn serialize < S > ( & self , s : S ) -> Result < S :: Ok , S :: Error >
173
169
where
174
170
S : Serializer ,
175
171
{
176
- if self . 0 == ElGamalPubkey :: default ( ) {
172
+ if self . 0 == PodElGamalPubkey :: default ( ) {
177
173
s. serialize_none ( )
178
174
} else {
179
175
s. serialize_some ( & self . 0 . to_string ( ) )
@@ -196,18 +192,7 @@ impl<'de> Visitor<'de> for OptionalNonZeroElGamalPubkeyVisitor {
196
192
where
197
193
E : Error ,
198
194
{
199
- let bytes = BASE64_STANDARD . decode ( v) . map_err ( Error :: custom) ?;
200
-
201
- if bytes. len ( ) != OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN {
202
- return Err ( Error :: custom ( format ! (
203
- "Length of base64 decoded bytes is not {}" ,
204
- OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN
205
- ) ) ) ;
206
- }
207
-
208
- let mut array = [ 0 ; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN ] ;
209
- array. copy_from_slice ( & bytes[ 0 ..OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN ] ) ;
210
- let elgamal_pubkey = ElGamalPubkey ( array) ;
195
+ let elgamal_pubkey: PodElGamalPubkey = FromStr :: from_str ( v) . map_err ( Error :: custom) ?;
211
196
OptionalNonZeroElGamalPubkey :: try_from ( Some ( elgamal_pubkey) ) . map_err ( Error :: custom)
212
197
}
213
198
@@ -231,7 +216,12 @@ impl<'de> Deserialize<'de> for OptionalNonZeroElGamalPubkey {
231
216
232
217
#[ cfg( test) ]
233
218
mod tests {
234
- use { super :: * , crate :: bytemuck:: pod_from_bytes, solana_program:: pubkey:: PUBKEY_BYTES } ;
219
+ use {
220
+ super :: * ,
221
+ crate :: bytemuck:: pod_from_bytes,
222
+ base64:: { prelude:: BASE64_STANDARD , Engine } ,
223
+ solana_program:: pubkey:: PUBKEY_BYTES ,
224
+ } ;
235
225
236
226
#[ test]
237
227
fn test_pod_non_zero_option ( ) {
@@ -290,23 +280,41 @@ mod tests {
290
280
assert_eq ! ( optional_non_zero_pubkey_none, deserialized_none) ;
291
281
}
292
282
283
+ const OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN : usize = 32 ;
284
+
285
+ // Unfortunately, the `solana-zk-sdk` does not expose a constructor interface
286
+ // to construct `PodRistrettoPoint` from bytes. As a work-around, encode the
287
+ // bytes as base64 string and then convert the string to a
288
+ // `PodElGamalCiphertext`.
289
+ //
290
+ // The constructor will be added (and this function removed) with
291
+ // `solana-zk-sdk` 2.1.
292
+ fn elgamal_pubkey_from_bytes ( bytes : & [ u8 ] ) -> PodElGamalPubkey {
293
+ let string = BASE64_STANDARD . encode ( bytes) ;
294
+ std:: str:: FromStr :: from_str ( & string) . unwrap ( )
295
+ }
296
+
293
297
#[ test]
294
298
fn test_pod_non_zero_elgamal_option ( ) {
295
299
assert_eq ! (
296
- Some ( ElGamalPubkey ( [ 1 ; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN ] ) ) ,
297
- Option :: <ElGamalPubkey >:: from( OptionalNonZeroElGamalPubkey ( ElGamalPubkey (
298
- [ 1 ; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN ]
299
- ) ) )
300
+ Some ( elgamal_pubkey_from_bytes(
301
+ & [ 1 ; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN ]
302
+ ) ) ,
303
+ Option :: <PodElGamalPubkey >:: from( OptionalNonZeroElGamalPubkey (
304
+ elgamal_pubkey_from_bytes( & [ 1 ; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN ] )
305
+ ) )
300
306
) ;
301
307
assert_eq ! (
302
308
None ,
303
- Option :: <ElGamalPubkey >:: from( OptionalNonZeroElGamalPubkey ( ElGamalPubkey (
304
- [ 0 ; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN ]
305
- ) ) )
309
+ Option :: <PodElGamalPubkey >:: from( OptionalNonZeroElGamalPubkey (
310
+ elgamal_pubkey_from_bytes ( & [ 0 ; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN ] )
311
+ ) )
306
312
) ;
307
313
308
314
assert_eq ! (
309
- OptionalNonZeroElGamalPubkey ( ElGamalPubkey ( [ 1 ; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN ] ) ) ,
315
+ OptionalNonZeroElGamalPubkey ( elgamal_pubkey_from_bytes(
316
+ & [ 1 ; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN ]
317
+ ) ) ,
310
318
* pod_from_bytes:: <OptionalNonZeroElGamalPubkey >(
311
319
& [ 1 ; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN ]
312
320
)
@@ -318,8 +326,9 @@ mod tests {
318
326
#[ cfg( feature = "serde-traits" ) ]
319
327
#[ test]
320
328
fn test_pod_non_zero_elgamal_option_serde_some ( ) {
321
- let optional_non_zero_elgamal_pubkey_some =
322
- OptionalNonZeroElGamalPubkey ( ElGamalPubkey ( [ 1 ; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN ] ) ) ;
329
+ let optional_non_zero_elgamal_pubkey_some = OptionalNonZeroElGamalPubkey (
330
+ elgamal_pubkey_from_bytes ( & [ 1 ; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN ] ) ,
331
+ ) ;
323
332
let serialized_some =
324
333
serde_json:: to_string ( & optional_non_zero_elgamal_pubkey_some) . unwrap ( ) ;
325
334
assert_eq ! (
@@ -335,8 +344,9 @@ mod tests {
335
344
#[ cfg( feature = "serde-traits" ) ]
336
345
#[ test]
337
346
fn test_pod_non_zero_elgamal_option_serde_none ( ) {
338
- let optional_non_zero_elgamal_pubkey_none =
339
- OptionalNonZeroElGamalPubkey ( ElGamalPubkey ( [ 0 ; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN ] ) ) ;
347
+ let optional_non_zero_elgamal_pubkey_none = OptionalNonZeroElGamalPubkey (
348
+ elgamal_pubkey_from_bytes ( & [ 0 ; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN ] ) ,
349
+ ) ;
340
350
let serialized_none =
341
351
serde_json:: to_string ( & optional_non_zero_elgamal_pubkey_none) . unwrap ( ) ;
342
352
assert_eq ! ( & serialized_none, "null" ) ;
0 commit comments