use std::{fmt, io};
use group::{ff::PrimeField, prime::PrimeCurveAffine, Group, GroupEncoding};
use halo2::{
arithmetic::{Coordinates, CurveAffine},
pasta::pallas,
};
use rand_core::{CryptoRng, RngCore};
use crate::{
error::RandError,
serialization::{
serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize,
},
};
use super::sinsemilla::*;
fn diversify_hash(d: &[u8]) -> pallas::Point {
let p = pallas_group_hash(b"z.cash:Orchard-gd", d);
if <bool>::from(p.is_identity()) {
pallas_group_hash(b"z.cash:Orchard-gd", b"")
} else {
p
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
#[cfg_attr(
any(test, feature = "proptest-impl"),
derive(proptest_derive::Arbitrary)
)]
pub struct Diversifier(pub(crate) [u8; 11]);
impl fmt::Debug for Diversifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Diversifier")
.field(&hex::encode(self.0))
.finish()
}
}
impl From<[u8; 11]> for Diversifier {
fn from(bytes: [u8; 11]) -> Self {
Self(bytes)
}
}
impl From<Diversifier> for [u8; 11] {
fn from(d: Diversifier) -> [u8; 11] {
d.0
}
}
impl From<Diversifier> for pallas::Point {
fn from(d: Diversifier) -> Self {
diversify_hash(&d.0)
}
}
impl PartialEq<[u8; 11]> for Diversifier {
fn eq(&self, other: &[u8; 11]) -> bool {
self.0 == *other
}
}
impl From<Diversifier> for pallas::Affine {
fn from(d: Diversifier) -> Self {
let projective_point = pallas::Point::from(d);
projective_point.into()
}
}
impl Diversifier {
pub fn new<T>(csprng: &mut T) -> Result<Self, RandError>
where
T: RngCore + CryptoRng,
{
let mut bytes = [0u8; 11];
csprng
.try_fill_bytes(&mut bytes)
.map_err(|_| RandError::FillBytes)?;
Ok(Self::from(bytes))
}
}
#[derive(Copy, Clone, PartialEq)]
pub struct TransmissionKey(pub(crate) pallas::Affine);
impl fmt::Debug for TransmissionKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut d = f.debug_struct("TransmissionKey");
let option: Option<Coordinates<pallas::Affine>> = self.0.coordinates().into();
match option {
Some(coordinates) => d
.field("x", &hex::encode(coordinates.x().to_repr()))
.field("y", &hex::encode(coordinates.y().to_repr()))
.finish(),
None => d
.field("x", &hex::encode(pallas::Base::zero().to_repr()))
.field("y", &hex::encode(pallas::Base::zero().to_repr()))
.finish(),
}
}
}
impl Eq for TransmissionKey {}
impl From<TransmissionKey> for [u8; 32] {
fn from(pk_d: TransmissionKey) -> [u8; 32] {
pk_d.0.to_bytes()
}
}
impl PartialEq<[u8; 32]> for TransmissionKey {
fn eq(&self, other: &[u8; 32]) -> bool {
&self.0.to_bytes() == other
}
}
#[derive(Copy, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct EphemeralPublicKey(#[serde(with = "serde_helpers::Affine")] pub(crate) pallas::Affine);
impl fmt::Debug for EphemeralPublicKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut d = f.debug_struct("EphemeralPublicKey");
let option: Option<Coordinates<pallas::Affine>> = self.0.coordinates().into();
match option {
Some(coordinates) => d
.field("x", &hex::encode(coordinates.x().to_repr()))
.field("y", &hex::encode(coordinates.y().to_repr()))
.finish(),
None => d
.field("x", &hex::encode(pallas::Base::zero().to_repr()))
.field("y", &hex::encode(pallas::Base::zero().to_repr()))
.finish(),
}
}
}
impl From<EphemeralPublicKey> for [u8; 32] {
fn from(epk: EphemeralPublicKey) -> [u8; 32] {
epk.0.to_bytes()
}
}
impl From<&EphemeralPublicKey> for [u8; 32] {
fn from(epk: &EphemeralPublicKey) -> [u8; 32] {
epk.0.to_bytes()
}
}
impl PartialEq<[u8; 32]> for EphemeralPublicKey {
fn eq(&self, other: &[u8; 32]) -> bool {
&self.0.to_bytes() == other
}
}
impl TryFrom<[u8; 32]> for EphemeralPublicKey {
type Error = &'static str;
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
let possible_point = pallas::Affine::from_bytes(&bytes);
if possible_point.is_some().into() {
let point = possible_point.unwrap();
if point.to_curve().is_identity().into() {
Err("pallas::Affine value for Orchard EphemeralPublicKey is the identity")
} else {
Ok(Self(possible_point.unwrap()))
}
} else {
Err("Invalid pallas::Affine value for Orchard EphemeralPublicKey")
}
}
}
impl ZcashSerialize for EphemeralPublicKey {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&<[u8; 32]>::from(self)[..])?;
Ok(())
}
}
impl ZcashDeserialize for EphemeralPublicKey {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Self::try_from(reader.read_32_bytes()?).map_err(SerializationError::Parse)
}
}