use std::{
cmp::{Eq, PartialEq},
fmt::{self, Debug},
io,
};
use byteorder::{ReadBytesExt, WriteBytesExt};
use halo2::pasta::pallas;
use reddsa::{orchard::Binding, orchard::SpendAuth, Signature};
use crate::{
amount::{Amount, NegativeAllowed},
block::MAX_BLOCK_BYTES,
orchard::{tree, Action, Nullifier, ValueCommitment},
primitives::Halo2Proof,
serialization::{
AtLeastOne, SerializationError, TrustedPreallocate, ZcashDeserialize, ZcashSerialize,
},
};
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct ShieldedData {
pub flags: Flags,
pub value_balance: Amount,
pub shared_anchor: tree::Root,
pub proof: Halo2Proof,
pub actions: AtLeastOne<AuthorizedAction>,
pub binding_sig: Signature<Binding>,
}
impl fmt::Display for ShieldedData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut fmter = f.debug_struct("orchard::ShieldedData");
fmter.field("actions", &self.actions.len());
fmter.field("value_balance", &self.value_balance);
fmter.field("flags", &self.flags);
fmter.field("proof_len", &self.proof.zcash_serialized_size());
fmter.field("shared_anchor", &self.shared_anchor);
fmter.finish()
}
}
impl ShieldedData {
pub fn actions(&self) -> impl Iterator<Item = &Action> {
self.actions.actions()
}
pub fn nullifiers(&self) -> impl Iterator<Item = &Nullifier> {
self.actions().map(|action| &action.nullifier)
}
pub fn binding_verification_key(&self) -> reddsa::VerificationKeyBytes<Binding> {
let cv: ValueCommitment = self.actions().map(|action| action.cv).sum();
let cv_balance: ValueCommitment =
ValueCommitment::new(pallas::Scalar::zero(), self.value_balance);
let key_bytes: [u8; 32] = (cv - cv_balance).into();
key_bytes.into()
}
pub fn value_balance(&self) -> Amount<NegativeAllowed> {
self.value_balance
}
pub fn note_commitments(&self) -> impl Iterator<Item = &pallas::Base> {
self.actions().map(|action| &action.cm_x)
}
}
pub trait OrchardActions {
fn actions(&self) -> impl Iterator<Item = &Action> + '_;
}
impl OrchardActions for AtLeastOne<AuthorizedAction> {
fn actions(&self) -> impl Iterator<Item = &Action> + '_ {
self.iter()
.map(|authorized_action| &authorized_action.action)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct AuthorizedAction {
pub action: Action,
pub spend_auth_sig: Signature<SpendAuth>,
}
impl AuthorizedAction {
pub fn into_parts(self) -> (Action, Signature<SpendAuth>) {
(self.action, self.spend_auth_sig)
}
pub fn from_parts(action: Action, spend_auth_sig: Signature<SpendAuth>) -> AuthorizedAction {
AuthorizedAction {
action,
spend_auth_sig,
}
}
}
pub const ACTION_SIZE: u64 = 5 * 32 + 580 + 80;
pub const SPEND_AUTH_SIG_SIZE: u64 = 64;
pub const AUTHORIZED_ACTION_SIZE: u64 = ACTION_SIZE + SPEND_AUTH_SIG_SIZE;
impl TrustedPreallocate for Action {
fn max_allocation() -> u64 {
const MAX: u64 = (MAX_BLOCK_BYTES - 1) / AUTHORIZED_ACTION_SIZE;
static_assertions::const_assert!(MAX < (1 << 16));
MAX
}
}
impl TrustedPreallocate for Signature<SpendAuth> {
fn max_allocation() -> u64 {
Action::max_allocation()
}
}
bitflags! {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Flags: u8 {
const ENABLE_SPENDS = 0b00000001;
const ENABLE_OUTPUTS = 0b00000010;
}
}
impl serde::Serialize for Flags {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
bitflags_serde_legacy::serialize(self, "Flags", serializer)
}
}
impl<'de> serde::Deserialize<'de> for Flags {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
bitflags_serde_legacy::deserialize("Flags", deserializer)
}
}
impl ZcashSerialize for Flags {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_u8(self.bits())?;
Ok(())
}
}
impl ZcashDeserialize for Flags {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Flags::from_bits(reader.read_u8()?)
.ok_or(SerializationError::Parse("invalid reserved orchard flags"))
}
}