Refactor orchard_zsa modules to use IssueBundle serialization functions from librustzcash/zcash_primitives instead of reimplementing them

This commit is contained in:
Dmitry Demin 2024-11-11 21:23:31 +01:00
parent 50680a6868
commit 3cce4894d0
4 changed files with 28 additions and 225 deletions

View File

@ -4,8 +4,6 @@
#[cfg(any(test, feature = "proptest-impl"))]
pub(crate) mod arbitrary;
mod common;
mod burn;
mod issuance;

View File

@ -5,12 +5,28 @@ use std::io;
use crate::{
amount::Amount,
block::MAX_BLOCK_BYTES,
serialization::{SerializationError, TrustedPreallocate, ZcashDeserialize, ZcashSerialize},
serialization::{
ReadZcashExt, SerializationError, TrustedPreallocate, ZcashDeserialize, ZcashSerialize,
},
};
use orchard::{note::AssetBase, value::NoteValue};
use super::common::ASSET_BASE_SIZE;
// The size of the serialized AssetBase in bytes (used for TrustedPreallocate impls)
pub(super) const ASSET_BASE_SIZE: u64 = 32;
impl ZcashSerialize for AssetBase {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.to_bytes())
}
}
impl ZcashDeserialize for AssetBase {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Option::from(AssetBase::from_bytes(&reader.read_32_bytes()?))
.ok_or_else(|| SerializationError::Parse("Invalid orchard_zsa AssetBase!"))
}
}
// Sizes of the serialized values for types in bytes (used for TrustedPreallocate impls)
const AMOUNT_SIZE: u64 = 8;

View File

@ -1,23 +0,0 @@
//! Serialization implementation for selected types from the 'orchard_zsa' crate used in this module.
use std::io;
use crate::serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize};
use orchard::note::AssetBase;
// The size of the serialized AssetBase in bytes (used for TrustedPreallocate impls)
pub(super) const ASSET_BASE_SIZE: u64 = 32;
impl ZcashSerialize for AssetBase {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.to_bytes())
}
}
impl ZcashDeserialize for AssetBase {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Option::from(AssetBase::from_bytes(&reader.read_32_bytes()?))
.ok_or_else(|| SerializationError::Parse("Invalid orchard_zsa AssetBase!"))
}
}

View File

@ -2,36 +2,25 @@
use std::{fmt::Debug, io};
use nonempty::NonEmpty;
// FIXME: needed for "as_bool" only - consider to implement as_bool locally
use bitvec::macros::internal::funty::Fundamental;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use halo2::pasta::pallas;
// For pallas::Base::from_repr only
use group::ff::PrimeField;
use zcash_primitives::transaction::components::issuance::{read_v6_bundle, write_v6_bundle};
use orchard::{
issuance::{IssueAction, IssueBundle, Signed},
keys::IssuanceValidatingKey,
note::{ExtractedNoteCommitment, RandomSeed, Rho},
primitives::redpallas::{SigType, Signature, SpendAuth},
value::NoteValue,
Address, Note,
note::ExtractedNoteCommitment,
Note,
};
use crate::{
block::MAX_BLOCK_BYTES,
serialization::{
zcash_serialize_bytes, zcash_serialize_empty_list, ReadZcashExt, SerializationError,
TrustedPreallocate, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize,
},
serialization::{SerializationError, TrustedPreallocate, ZcashDeserialize, ZcashSerialize},
};
use super::common::ASSET_BASE_SIZE;
use super::burn::ASSET_BASE_SIZE;
/// Wrapper for `IssueBundle` used in the context of Transaction V6. This allows the implementation of
/// a Serde serializer for unit tests within this crate.
@ -70,126 +59,6 @@ const RANDOM_SEED_SIZE: u64 = 32;
const NOTE_SIZE: u64 =
ADDRESS_SIZE + NOTE_VALUE_SIZE + ASSET_BASE_SIZE + NULLIFIER_SIZE + RANDOM_SEED_SIZE;
// FIXME: duplicates ZcashSerialize for reddsa::Signature in transaction/serialize.rs
// (as Signature from oechard_zsa is formally a different type)
impl<T: SigType> ZcashSerialize for Signature<T> {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&<[u8; 64]>::from(self))?;
Ok(())
}
}
// FIXME: duplicates ZcashDeserialize for reddsa::Signature in transaction/serialize.rs
// (as Signature from oechard_zsa is formally a different type)
impl<T: SigType> ZcashDeserialize for Signature<T> {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Ok(reader.read_64_bytes()?.into())
}
}
impl ZcashDeserialize for Signed {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
let signature = Signature::<SpendAuth>::zcash_deserialize(&mut reader)?;
Ok(Signed::from_data((&signature).into()))
}
}
impl ZcashSerialize for IssuanceValidatingKey {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.to_bytes())
}
}
impl ZcashDeserialize for IssuanceValidatingKey {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
IssuanceValidatingKey::from_bytes(&reader.read_32_bytes()?)
.ok_or_else(|| SerializationError::Parse("Invalid orchard_zsa IssuanceValidatingKey!"))
}
}
impl ZcashSerialize for Address {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.to_raw_address_bytes())
}
}
impl ZcashDeserialize for Address {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
let mut bytes = [0u8; ADDRESS_SIZE as usize];
reader.read_exact(&mut bytes)?;
Option::from(Address::from_raw_address_bytes(&bytes))
.ok_or_else(|| SerializationError::Parse("Invalid orchard_zsa Address!"))
}
}
impl ZcashSerialize for Rho {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.to_bytes())
}
}
impl ZcashDeserialize for Rho {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Option::from(Rho::from_bytes(&reader.read_32_bytes()?))
.ok_or_else(|| SerializationError::Parse("Invalid orchard_zsa Rho!"))
}
}
impl ZcashSerialize for RandomSeed {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(self.as_bytes())
}
}
// RandomSeed::zcash_deserialize can't be implemented and used as it requires Nullifier parameter.
// That's why we need to have this function.
fn zcash_deserialize_random_seed<R: io::Read>(
mut reader: R,
rho: &Rho,
) -> Result<RandomSeed, SerializationError> {
Option::from(RandomSeed::from_bytes(reader.read_32_bytes()?, rho))
.ok_or_else(|| SerializationError::Parse("Invalid orchard_zsa RandomSeed!"))
}
impl ZcashSerialize for NoteValue {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
// FIXME: use Amount serializer/deserializer?
writer.write_u64::<LittleEndian>(self.inner())?;
Ok(())
}
}
impl ZcashDeserialize for NoteValue {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Ok(NoteValue::from_raw(reader.read_u64::<LittleEndian>()?))
}
}
impl ZcashSerialize for Note {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
self.recipient().zcash_serialize(&mut writer)?;
self.value().zcash_serialize(&mut writer)?;
self.asset().zcash_serialize(&mut writer)?;
self.rho().zcash_serialize(&mut writer)?;
self.rseed().zcash_serialize(&mut writer)?;
Ok(())
}
}
impl ZcashDeserialize for Note {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
let recipient = (&mut reader).zcash_deserialize_into()?;
let value = (&mut reader).zcash_deserialize_into()?;
let asset = (&mut reader).zcash_deserialize_into()?;
let rho = (&mut reader).zcash_deserialize_into()?;
let rseed = zcash_deserialize_random_seed(&mut reader, &rho)?;
Option::from(Note::from_parts(recipient, value, asset, rho, rseed))
.ok_or_else(|| SerializationError::Parse("Invalid orchard_zsa Note components!"))
}
}
impl TrustedPreallocate for Note {
fn max_allocation() -> u64 {
// FIXME: is this a correct calculation way?
@ -199,24 +68,6 @@ impl TrustedPreallocate for Note {
}
}
impl ZcashSerialize for IssueAction {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_u8(self.is_finalized().as_u8())?;
self.notes().to_vec().zcash_serialize(&mut writer)?;
zcash_serialize_bytes(&self.asset_desc().to_vec(), &mut writer)?;
Ok(())
}
}
impl ZcashDeserialize for IssueAction {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
let finalize = reader.read_u8()?.as_bool();
let notes = (&mut reader).zcash_deserialize_into()?;
let asset_descr = (&mut reader).zcash_deserialize_into()?;
Ok(IssueAction::from_parts(asset_descr, notes, finalize))
}
}
impl TrustedPreallocate for IssueAction {
fn max_allocation() -> u64 {
// FIXME: impl correct calculation
@ -224,56 +75,17 @@ impl TrustedPreallocate for IssueAction {
}
}
impl ZcashSerialize for IssueBundle<Signed> {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
// FIXME: try to avoid transforming to Vec (consider implementation of ZcashSerialize for IntoIter generic,
// or use AtLeastOne).
// This is how does it work in librustzcash:
// Vector::write_nonempty(&mut writer, bundle.actions(), |w, action| write_action(action, w))?;
let actions: Vec<_> = self.actions().clone().into();
actions.zcash_serialize(&mut writer)?;
self.ik().zcash_serialize(&mut writer)?;
writer.write_all(&<[u8; 64]>::from(self.authorization().signature()))?;
Ok(())
}
}
impl ZcashSerialize for Option<IssueData> {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
match self {
None => {
// Denoted as `&Option<IssueData>` in the spec (ZIP 230).
zcash_serialize_empty_list(writer)?;
}
Some(issue_data) => {
issue_data.0.zcash_serialize(&mut writer)?;
}
}
Ok(())
fn zcash_serialize<W: io::Write>(&self, writer: W) -> Result<(), io::Error> {
write_v6_bundle(self.as_ref().map(|issue_data| &issue_data.0), writer)
}
}
// FIXME: We can't split IssueData out of Option<IssueData> deserialization,
// because the counts are read along with the arrays.
impl ZcashDeserialize for Option<IssueData> {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
let actions: Vec<_> = (&mut reader).zcash_deserialize_into()?;
if actions.is_empty() {
Ok(None)
} else {
let ik = (&mut reader).zcash_deserialize_into()?;
let authorization = (&mut reader).zcash_deserialize_into()?;
Ok(Some(IssueData(IssueBundle::from_parts(
ik,
NonEmpty::from_vec(actions).ok_or_else(|| {
SerializationError::Parse("Invalid orchard_zsa IssueData - no actions!")
})?,
authorization,
))))
}
fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
Ok(read_v6_bundle(reader)?.map(IssueData))
}
}