Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 16 additions & 17 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions pod/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@ bytemuck_derive = { version = "1.10.1" }
num-derive = "0.4"
num_enum = "0.7"
num-traits = "0.2"
serde = { version = "1.0.228", optional = true }
serde = { version = "1.0.228", optional = true, features = ["derive"] }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing that the solana-zk-sdk pulled this in automatically before?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes zk-sdk pulled in derive feature automatically.

solana-address = { version = "2.2.0", features = ["bytemuck"] }
solana-program-error = "3.0.0"
solana-program-option = "3.0.0"
solana-zk-sdk = "4.0.0"
thiserror = "2.0"
wincode = { version = "0.4.4", features = ["derive"], optional = true }

Expand Down
172 changes: 1 addition & 171 deletions pod/src/optional_keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use {
solana_address::Address,
solana_program_error::ProgramError,
solana_program_option::COption,
solana_zk_sdk::encryption::pod::elgamal::PodElGamalPubkey,
};
#[cfg(feature = "serde-traits")]
use {
Expand Down Expand Up @@ -128,102 +127,9 @@ impl<'de> Deserialize<'de> for OptionalNonZeroPubkey {
}
}

/// An `ElGamalPubkey` that encodes `None` as all `0`, meant to be usable as a
/// `Pod` type.
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
#[repr(transparent)]
pub struct OptionalNonZeroElGamalPubkey(PodElGamalPubkey);
impl OptionalNonZeroElGamalPubkey {
/// Checks equality between an `OptionalNonZeroElGamalPubkey` and an
/// `ElGamalPubkey` when interpreted as bytes.
pub fn equals(&self, other: &PodElGamalPubkey) -> bool {
&self.0 == other
}
}
impl TryFrom<Option<PodElGamalPubkey>> for OptionalNonZeroElGamalPubkey {
type Error = ProgramError;
fn try_from(p: Option<PodElGamalPubkey>) -> Result<Self, Self::Error> {
match p {
None => Ok(Self(PodElGamalPubkey::default())),
Some(elgamal_pubkey) => {
if elgamal_pubkey == PodElGamalPubkey::default() {
Err(ProgramError::InvalidArgument)
} else {
Ok(Self(elgamal_pubkey))
}
}
}
}
}
impl From<OptionalNonZeroElGamalPubkey> for Option<PodElGamalPubkey> {
fn from(p: OptionalNonZeroElGamalPubkey) -> Self {
if p.0 == PodElGamalPubkey::default() {
None
} else {
Some(p.0)
}
}
}

#[cfg(feature = "serde-traits")]
impl Serialize for OptionalNonZeroElGamalPubkey {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if self.0 == PodElGamalPubkey::default() {
s.serialize_none()
} else {
s.serialize_some(&self.0.to_string())
}
}
}

#[cfg(feature = "serde-traits")]
struct OptionalNonZeroElGamalPubkeyVisitor;

#[cfg(feature = "serde-traits")]
impl Visitor<'_> for OptionalNonZeroElGamalPubkeyVisitor {
type Value = OptionalNonZeroElGamalPubkey;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an ElGamal public key as base64 or `null`")
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: Error,
{
let elgamal_pubkey: PodElGamalPubkey = FromStr::from_str(v).map_err(Error::custom)?;
OptionalNonZeroElGamalPubkey::try_from(Some(elgamal_pubkey)).map_err(Error::custom)
}

fn visit_unit<E>(self) -> Result<Self::Value, E>
where
E: Error,
{
Ok(OptionalNonZeroElGamalPubkey::default())
}
}

#[cfg(feature = "serde-traits")]
impl<'de> Deserialize<'de> for OptionalNonZeroElGamalPubkey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(OptionalNonZeroElGamalPubkeyVisitor)
}
}

#[cfg(test)]
mod tests {
use {
super::*,
crate::bytemuck::pod_from_bytes,
base64::{prelude::BASE64_STANDARD, Engine},
solana_address::ADDRESS_BYTES,
};
use {super::*, crate::bytemuck::pod_from_bytes, solana_address::ADDRESS_BYTES};

#[test]
fn test_pod_non_zero_option() {
Expand Down Expand Up @@ -281,80 +187,4 @@ mod tests {
serde_json::from_str::<OptionalNonZeroPubkey>(&serialized_none).unwrap();
assert_eq!(optional_non_zero_pubkey_none, deserialized_none);
}

const OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN: usize = 32;

// Unfortunately, the `solana-zk-sdk` does not expose a constructor interface
// to construct `PodRistrettoPoint` from bytes. As a work-around, encode the
// bytes as base64 string and then convert the string to a
// `PodElGamalCiphertext`.
//
// The constructor will be added (and this function removed) with
// `solana-zk-sdk` 2.1.
fn elgamal_pubkey_from_bytes(bytes: &[u8]) -> PodElGamalPubkey {
let string = BASE64_STANDARD.encode(bytes);
core::str::FromStr::from_str(&string).unwrap()
}

#[test]
fn test_pod_non_zero_elgamal_option() {
assert_eq!(
Some(elgamal_pubkey_from_bytes(
&[1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN]
)),
Option::<PodElGamalPubkey>::from(OptionalNonZeroElGamalPubkey(
elgamal_pubkey_from_bytes(&[1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN])
))
);
assert_eq!(
None,
Option::<PodElGamalPubkey>::from(OptionalNonZeroElGamalPubkey(
elgamal_pubkey_from_bytes(&[0; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN])
))
);

assert_eq!(
OptionalNonZeroElGamalPubkey(elgamal_pubkey_from_bytes(
&[1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN]
)),
*pod_from_bytes::<OptionalNonZeroElGamalPubkey>(
&[1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN]
)
.unwrap()
);
assert!(pod_from_bytes::<OptionalNonZeroElGamalPubkey>(&[]).is_err());
}

#[cfg(feature = "serde-traits")]
#[test]
fn test_pod_non_zero_elgamal_option_serde_some() {
let optional_non_zero_elgamal_pubkey_some = OptionalNonZeroElGamalPubkey(
elgamal_pubkey_from_bytes(&[1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN]),
);
let serialized_some =
serde_json::to_string(&optional_non_zero_elgamal_pubkey_some).unwrap();
assert_eq!(
&serialized_some,
"\"AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=\""
);

let deserialized_some =
serde_json::from_str::<OptionalNonZeroElGamalPubkey>(&serialized_some).unwrap();
assert_eq!(optional_non_zero_elgamal_pubkey_some, deserialized_some);
}

#[cfg(feature = "serde-traits")]
#[test]
fn test_pod_non_zero_elgamal_option_serde_none() {
let optional_non_zero_elgamal_pubkey_none = OptionalNonZeroElGamalPubkey(
elgamal_pubkey_from_bytes(&[0; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN]),
);
let serialized_none =
serde_json::to_string(&optional_non_zero_elgamal_pubkey_none).unwrap();
assert_eq!(&serialized_none, "null");

let deserialized_none =
serde_json::from_str::<OptionalNonZeroElGamalPubkey>(&serialized_none).unwrap();
assert_eq!(optional_non_zero_elgamal_pubkey_none, deserialized_none);
}
}
2 changes: 1 addition & 1 deletion tlv-account-resolution/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ bytemuck = { version = "1.23.2", features = ["derive"] }
num-derive = "0.4"
num_enum = "0.7"
num-traits = "0.2"
serde = { version = "1.0.228", optional = true }
serde = { version = "1.0.228", optional = true, features = ["derive"] }
solana-account-info = "3.0.0"
solana-instruction = { version = "3.0.0", features = ["std"] }
solana-program-error = "3.0.0"
Expand Down
Loading