use std::{fmt, io};
use derive_getters::Getters;
use crate::{
block::MAX_BLOCK_BYTES,
primitives::{redjubjub::SpendAuth, Groth16Proof},
serialization::{
ReadZcashExt, SerializationError, TrustedPreallocate, WriteZcashExt, ZcashDeserialize,
ZcashDeserializeInto, ZcashSerialize,
},
};
use super::{
commitment, keys::ValidatingKey, note, tree, AnchorVariant, FieldNotPresent, PerSpendAnchor,
SharedAnchor,
};
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Getters)]
pub struct Spend<AnchorV: AnchorVariant> {
pub cv: commitment::ValueCommitment,
pub per_spend_anchor: AnchorV::PerSpend,
pub nullifier: note::Nullifier,
pub rk: ValidatingKey,
pub zkproof: Groth16Proof,
pub spend_auth_sig: redjubjub::Signature<SpendAuth>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct SpendPrefixInTransactionV5 {
pub cv: commitment::ValueCommitment,
pub nullifier: note::Nullifier,
pub rk: ValidatingKey,
}
impl<AnchorV: AnchorVariant + PartialEq> Eq for Spend<AnchorV> {}
impl Eq for SpendPrefixInTransactionV5 {}
impl<AnchorV> fmt::Display for Spend<AnchorV>
where
AnchorV: AnchorVariant + Clone,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut fmter = f
.debug_struct(format!("sapling::Spend<{}>", std::any::type_name::<AnchorV>()).as_str());
fmter.field("per_spend_anchor", &self.per_spend_anchor);
fmter.field("nullifier", &self.nullifier);
fmter.finish()
}
}
impl From<(Spend<SharedAnchor>, tree::Root)> for Spend<PerSpendAnchor> {
fn from(shared_spend: (Spend<SharedAnchor>, tree::Root)) -> Self {
Spend::<PerSpendAnchor> {
per_spend_anchor: shared_spend.1,
cv: shared_spend.0.cv,
nullifier: shared_spend.0.nullifier,
rk: shared_spend.0.rk,
zkproof: shared_spend.0.zkproof,
spend_auth_sig: shared_spend.0.spend_auth_sig,
}
}
}
impl From<(Spend<PerSpendAnchor>, FieldNotPresent)> for Spend<PerSpendAnchor> {
fn from(per_spend: (Spend<PerSpendAnchor>, FieldNotPresent)) -> Self {
per_spend.0
}
}
impl Spend<SharedAnchor> {
pub fn from_v5_parts(
prefix: SpendPrefixInTransactionV5,
zkproof: Groth16Proof,
spend_auth_sig: redjubjub::Signature<SpendAuth>,
) -> Spend<SharedAnchor> {
Spend::<SharedAnchor> {
cv: prefix.cv,
per_spend_anchor: FieldNotPresent,
nullifier: prefix.nullifier,
rk: prefix.rk,
zkproof,
spend_auth_sig,
}
}
pub fn into_v5_parts(
self,
) -> (
SpendPrefixInTransactionV5,
Groth16Proof,
redjubjub::Signature<SpendAuth>,
) {
let prefix = SpendPrefixInTransactionV5 {
cv: self.cv,
nullifier: self.nullifier,
rk: self.rk,
};
(prefix, self.zkproof, self.spend_auth_sig)
}
}
impl ZcashSerialize for Spend<PerSpendAnchor> {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.cv.0.to_bytes())?;
self.per_spend_anchor.zcash_serialize(&mut writer)?;
writer.write_32_bytes(&self.nullifier.into())?;
writer.write_all(&<[u8; 32]>::from(self.rk.clone())[..])?;
self.zkproof.zcash_serialize(&mut writer)?;
writer.write_all(&<[u8; 64]>::from(self.spend_auth_sig)[..])?;
Ok(())
}
}
impl ZcashDeserialize for Spend<PerSpendAnchor> {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Ok(Spend {
cv: commitment::ValueCommitment(
sapling_crypto::value::ValueCommitment::zcash_deserialize(&mut reader)?,
),
per_spend_anchor: (&mut reader).zcash_deserialize_into()?,
nullifier: note::Nullifier::from(reader.read_32_bytes()?),
rk: reader
.read_32_bytes()?
.try_into()
.map_err(SerializationError::Parse)?,
zkproof: Groth16Proof::zcash_deserialize(&mut reader)?,
spend_auth_sig: reader.read_64_bytes()?.into(),
})
}
}
impl ZcashSerialize for SpendPrefixInTransactionV5 {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.cv.0.to_bytes())?;
writer.write_32_bytes(&self.nullifier.into())?;
writer.write_all(&<[u8; 32]>::from(self.rk.clone())[..])?;
Ok(())
}
}
impl ZcashDeserialize for SpendPrefixInTransactionV5 {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Ok(SpendPrefixInTransactionV5 {
cv: commitment::ValueCommitment::zcash_deserialize(&mut reader)?,
nullifier: note::Nullifier::from(reader.read_32_bytes()?),
rk: reader
.read_32_bytes()?
.try_into()
.map_err(SerializationError::Parse)?,
})
}
}
impl ZcashSerialize for redjubjub::Signature<SpendAuth> {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&<[u8; 64]>::from(*self)[..])?;
Ok(())
}
}
impl ZcashDeserialize for redjubjub::Signature<SpendAuth> {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Ok(reader.read_64_bytes()?.into())
}
}
pub(crate) const ANCHOR_PER_SPEND_SIZE: u64 = SHARED_ANCHOR_SPEND_SIZE + 32;
pub(crate) const SHARED_ANCHOR_SPEND_PREFIX_SIZE: u64 = 32 + 32 + 32;
pub(crate) const SHARED_ANCHOR_SPEND_SIZE: u64 = SHARED_ANCHOR_SPEND_PREFIX_SIZE + 192 + 64;
impl TrustedPreallocate for Spend<PerSpendAnchor> {
fn max_allocation() -> u64 {
const MAX: u64 = (MAX_BLOCK_BYTES - 1) / ANCHOR_PER_SPEND_SIZE;
static_assertions::const_assert!(MAX < (1 << 16));
MAX
}
}
impl TrustedPreallocate for SpendPrefixInTransactionV5 {
fn max_allocation() -> u64 {
const MAX: u64 = (MAX_BLOCK_BYTES - 1) / SHARED_ANCHOR_SPEND_SIZE;
static_assertions::const_assert!(MAX < (1 << 16));
MAX
}
}
impl TrustedPreallocate for redjubjub::Signature<SpendAuth> {
fn max_allocation() -> u64 {
SpendPrefixInTransactionV5::max_allocation()
}
}