use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use getset::Getters;
use redjubjub::{Binding, SpendAuth};
use zcash_note_encryption::{
EphemeralKeyBytes, OutgoingCipherKey, ENC_CIPHERTEXT_SIZE, OUT_CIPHERTEXT_SIZE,
};
use zip32::ChildIndex;
use crate::{
bundle::GrothProofBytes,
keys::SpendAuthorizingKey,
note::ExtractedNoteCommitment,
value::{NoteValue, ValueCommitTrapdoor, ValueCommitment, ValueSum},
Anchor, MerklePath, Nullifier, PaymentAddress, ProofGenerationKey, Rseed,
};
mod parse;
pub use parse::ParseError;
mod verify;
pub use verify::VerifyError;
mod io_finalizer;
pub use io_finalizer::IoFinalizerError;
mod updater;
pub use updater::{OutputUpdater, SpendUpdater, Updater, UpdaterError};
#[cfg(feature = "circuit")]
mod prover;
#[cfg(feature = "circuit")]
pub use prover::ProverError;
mod signer;
pub use signer::SignerError;
mod tx_extractor;
pub use tx_extractor::{TxExtractorError, Unbound};
#[derive(Debug, Getters)]
#[getset(get = "pub")]
pub struct Bundle {
pub(crate) spends: Vec<Spend>,
pub(crate) outputs: Vec<Output>,
pub(crate) value_sum: ValueSum,
pub(crate) anchor: Anchor,
pub(crate) bsk: Option<redjubjub::SigningKey<Binding>>,
}
impl Bundle {
pub fn spends_mut(&mut self) -> &mut [Spend] {
&mut self.spends
}
}
#[derive(Debug, Getters)]
#[getset(get = "pub")]
pub struct Spend {
pub(crate) cv: ValueCommitment,
pub(crate) nullifier: Nullifier,
pub(crate) rk: redjubjub::VerificationKey<SpendAuth>,
pub(crate) zkproof: Option<GrothProofBytes>,
pub(crate) spend_auth_sig: Option<redjubjub::Signature<SpendAuth>>,
pub(crate) recipient: Option<PaymentAddress>,
pub(crate) value: Option<NoteValue>,
pub(crate) rseed: Option<Rseed>,
pub(crate) rcv: Option<ValueCommitTrapdoor>,
pub(crate) proof_generation_key: Option<ProofGenerationKey>,
pub(crate) witness: Option<MerklePath>,
pub(crate) alpha: Option<jubjub::Scalar>,
pub(crate) zip32_derivation: Option<Zip32Derivation>,
pub(crate) dummy_ask: Option<SpendAuthorizingKey>,
pub(crate) proprietary: BTreeMap<String, Vec<u8>>,
}
#[derive(Getters)]
#[getset(get = "pub")]
pub struct Output {
pub(crate) cv: ValueCommitment,
pub(crate) cmu: ExtractedNoteCommitment,
pub(crate) ephemeral_key: EphemeralKeyBytes,
pub(crate) enc_ciphertext: [u8; ENC_CIPHERTEXT_SIZE],
pub(crate) out_ciphertext: [u8; OUT_CIPHERTEXT_SIZE],
pub(crate) zkproof: Option<GrothProofBytes>,
pub(crate) recipient: Option<PaymentAddress>,
pub(crate) value: Option<NoteValue>,
pub(crate) rseed: Option<[u8; 32]>,
pub(crate) rcv: Option<ValueCommitTrapdoor>,
pub(crate) ock: Option<OutgoingCipherKey>,
pub(crate) zip32_derivation: Option<Zip32Derivation>,
pub(crate) user_address: Option<String>,
pub(crate) proprietary: BTreeMap<String, Vec<u8>>,
}
impl fmt::Debug for Output {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Output")
.field("cv", &self.cv)
.field("cmu", &self.cmu)
.field("ephemeral_key", &self.ephemeral_key)
.field("enc_ciphertext", &self.enc_ciphertext)
.field("out_ciphertext", &self.out_ciphertext)
.field("zkproof", &self.zkproof)
.field("recipient", &self.recipient)
.field("value", &self.value)
.field("rseed", &self.rseed)
.field("rcv", &self.rcv)
.field("zip32_derivation", &self.zip32_derivation)
.field("user_address", &self.user_address)
.field("proprietary", &self.proprietary)
.finish_non_exhaustive()
}
}
#[derive(Debug, Getters, PartialEq, Eq)]
#[getset(get = "pub")]
pub struct Zip32Derivation {
seed_fingerprint: [u8; 32],
derivation_path: Vec<ChildIndex>,
}
impl Zip32Derivation {
pub fn extract_account_index(
&self,
seed_fp: &zip32::fingerprint::SeedFingerprint,
expected_coin_type: zip32::ChildIndex,
) -> Option<zip32::AccountId> {
if self.seed_fingerprint == seed_fp.to_bytes() {
match &self.derivation_path[..] {
[purpose, coin_type, account_index]
if purpose == &zip32::ChildIndex::hardened(32)
&& coin_type == &expected_coin_type =>
{
Some(
zip32::AccountId::try_from(account_index.index() - (1 << 31))
.expect("zip32::ChildIndex only supports hardened"),
)
}
_ => None,
}
} else {
None
}
}
}