From 3c9a61895ebf21ed9f60ce40c481ee932c719b71 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Wed, 22 May 2024 17:15:35 +0200 Subject: [PATCH] Allow the serialization of transaction build parameters. --- masp_primitives/src/sapling.rs | 50 ++++- .../transaction/components/sapling/builder.rs | 174 +++++++++++++++++- 2 files changed, 219 insertions(+), 5 deletions(-) diff --git a/masp_primitives/src/sapling.rs b/masp_primitives/src/sapling.rs index 0cc757b9..53c4fce8 100644 --- a/masp_primitives/src/sapling.rs +++ b/masp_primitives/src/sapling.rs @@ -196,7 +196,7 @@ impl ValueCommitment { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ProofGenerationKey { pub ak: jubjub::SubgroupPoint, pub nsk: jubjub::Fr, @@ -211,6 +211,54 @@ impl ProofGenerationKey { } } +impl BorshSerialize for ProofGenerationKey { + fn serialize(&self, writer: &mut W) -> io::Result<()> { + writer.write_all(&self.ak.to_bytes())?; + writer.write_all(&self.nsk.to_repr())?; + Ok(()) + } +} + +impl BorshDeserialize for ProofGenerationKey { + fn deserialize_reader(reader: &mut R) -> io::Result { + let ak = { + let mut buf = [0u8; 32]; + reader.read_exact(&mut buf)?; + jubjub::SubgroupPoint::from_bytes(&buf) + }; + if ak.is_none().into() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "ak not in prime-order subgroup", + )); + } + let nsk_bytes = <[u8; 32]>::deserialize_reader(reader)?; + let nsk = Option::from(jubjub::Fr::from_bytes(&nsk_bytes)) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "nsk not in field"))?; + Ok(Self { + ak: ak.unwrap(), + nsk, + }) + } +} + +impl BorshSchema for ProofGenerationKey { + fn add_definitions_recursively(definitions: &mut BTreeMap) { + let definition = Definition::Struct { + fields: Fields::NamedFields(vec![ + ("ak".into(), <[u8; 32]>::declaration()), + ("nsk".into(), <[u8; 32]>::declaration()), + ]), + }; + add_definition(Self::declaration(), definition, definitions); + <[u8; 32]>::add_definitions_recursively(definitions); + } + + fn declaration() -> Declaration { + "ProofGenerationKey".into() + } +} + /// A key used to derive the nullifier for a Sapling note. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct NullifierDerivingKey(pub jubjub::SubgroupPoint); diff --git a/masp_primitives/src/transaction/components/sapling/builder.rs b/masp_primitives/src/transaction/components/sapling/builder.rs index eb150197..55dfc50a 100644 --- a/masp_primitives/src/transaction/components/sapling/builder.rs +++ b/masp_primitives/src/transaction/components/sapling/builder.rs @@ -4,6 +4,7 @@ use core::fmt; use std::sync::mpsc::Sender; use ff::Field; +use ff::PrimeField; use group::GroupEncoding; use rand::{seq::SliceRandom, CryptoRng, RngCore}; @@ -63,7 +64,7 @@ pub trait BuildParams { } /// Parameters that go into constructing a spend description -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct SpendBuildParams { /// The commitment value randomness rcv: jubjub::Fr, @@ -75,15 +76,113 @@ pub struct SpendBuildParams { proof_generation_key: Option, } +impl BorshSerialize for SpendBuildParams { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + // Write the commitment value randomness + writer.write_all(&self.rcv.to_repr())?; + // Write spend authorization randomizer + writer.write_all(&self.alpha.to_repr())?; + // Write the authorization signature + self.auth_sig.serialize(writer)?; + // Write the proof generation key + self.proof_generation_key.serialize(writer)?; + Ok(()) + } +} + +impl BorshDeserialize for SpendBuildParams { + fn deserialize_reader(reader: &mut R) -> std::io::Result { + // Read the commitment value randomness + let rcv_bytes = <[u8; 32]>::deserialize_reader(reader)?; + let rcv = Option::from(jubjub::Fr::from_bytes(&rcv_bytes)).ok_or_else(|| { + std::io::Error::new(std::io::ErrorKind::InvalidData, "rcv not in field") + })?; + // Read the spend authorization randomizer + let alpha_bytes = <[u8; 32]>::deserialize_reader(reader)?; + let alpha = Option::from(jubjub::Fr::from_bytes(&alpha_bytes)).ok_or_else(|| { + std::io::Error::new(std::io::ErrorKind::InvalidData, "alpha not in field") + })?; + // Read the authorization signature + let auth_sig = Option::::deserialize_reader(reader)?; + // Read the proof generation key + let proof_generation_key = Option::::deserialize_reader(reader)?; + // Finally, aggregate the spend parameters + Ok(SpendBuildParams { + rcv, + alpha, + auth_sig, + proof_generation_key, + }) + } +} + +impl BorshSchema for SpendBuildParams { + fn add_definitions_recursively(definitions: &mut BTreeMap) { + let definition = Definition::Struct { + fields: Fields::NamedFields(vec![ + ("rcv".into(), <[u8; 32]>::declaration()), + ("alpha".into(), <[u8; 32]>::declaration()), + ("auth_sig".into(), Option::::declaration()), + ( + "proof_generation_key".into(), + Option::::declaration(), + ), + ]), + }; + add_definition(Self::declaration(), definition, definitions); + <[u8; 32]>::add_definitions_recursively(definitions); + Option::::add_definitions_recursively(definitions); + Option::::add_definitions_recursively(definitions); + } + + fn declaration() -> Declaration { + "SpendBuildParams".into() + } +} + /// Parameters that go into constructing an output description -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub struct ConvertBuildParams { /// The commitment value randomness rcv: jubjub::Fr, } +impl BorshSerialize for ConvertBuildParams { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + // Write the commitment value randomness + writer.write_all(&self.rcv.to_repr())?; + Ok(()) + } +} + +impl BorshDeserialize for ConvertBuildParams { + fn deserialize_reader(reader: &mut R) -> std::io::Result { + // Read the commitment value randomness + let rcv_bytes = <[u8; 32]>::deserialize_reader(reader)?; + let rcv = Option::from(jubjub::Fr::from_bytes(&rcv_bytes)).ok_or_else(|| { + std::io::Error::new(std::io::ErrorKind::InvalidData, "rcv not in field") + })?; + // Finally, aggregate the convert parameters + Ok(ConvertBuildParams { rcv }) + } +} + +impl BorshSchema for ConvertBuildParams { + fn add_definitions_recursively(definitions: &mut BTreeMap) { + let definition = Definition::Struct { + fields: Fields::NamedFields(vec![("rcv".into(), <[u8; 32]>::declaration())]), + }; + add_definition(Self::declaration(), definition, definitions); + <[u8; 32]>::add_definitions_recursively(definitions); + } + + fn declaration() -> Declaration { + "ConvertBuildParams".into() + } +} + /// Parameters that go into constructing an output description -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub struct OutputBuildParams { /// The commitment value randomness rcv: jubjub::Fr, @@ -93,8 +192,57 @@ pub struct OutputBuildParams { rseed: [u8; 32], } +impl BorshSerialize for OutputBuildParams { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + // Write the commitment value randomness + writer.write_all(&self.rcv.to_repr())?; + // Write the note rcm value + writer.write_all(&self.rcm.to_repr())?; + // Write the note's random seed + self.rseed.serialize(writer)?; + Ok(()) + } +} + +impl BorshDeserialize for OutputBuildParams { + fn deserialize_reader(reader: &mut R) -> std::io::Result { + // Read the commitment value randomness + let rcv_bytes = <[u8; 32]>::deserialize_reader(reader)?; + let rcv = Option::from(jubjub::Fr::from_bytes(&rcv_bytes)).ok_or_else(|| { + std::io::Error::new(std::io::ErrorKind::InvalidData, "rcv not in field") + })?; + // Read the note rcm value + let rcm_bytes = <[u8; 32]>::deserialize_reader(reader)?; + let rcm = Option::from(jubjub::Fr::from_bytes(&rcm_bytes)).ok_or_else(|| { + std::io::Error::new(std::io::ErrorKind::InvalidData, "rcm not in field") + })?; + // Read the note's random seed + let rseed = <[u8; 32]>::deserialize_reader(reader)?; + // Finally, aggregate the output parameters + Ok(OutputBuildParams { rcv, rcm, rseed }) + } +} + +impl BorshSchema for OutputBuildParams { + fn add_definitions_recursively(definitions: &mut BTreeMap) { + let definition = Definition::Struct { + fields: Fields::NamedFields(vec![ + ("rcv".into(), <[u8; 32]>::declaration()), + ("rcm".into(), <[u8; 32]>::declaration()), + ("rseed".into(), <[u8; 32]>::declaration()), + ]), + }; + add_definition(Self::declaration(), definition, definitions); + <[u8; 32]>::add_definitions_recursively(definitions); + } + + fn declaration() -> Declaration { + "OutputBuildParams".into() + } +} + /// Pre-generated random parameters for MASPtTransactions -#[derive(Default, Clone)] +#[derive(Default, Clone, BorshDeserialize, BorshSchema, BorshSerialize, Debug)] pub struct StoredBuildParams { /// The parameters required to construct spend descriptions pub spend_params: Vec, @@ -160,6 +308,24 @@ impl RngBuildParams { outputs: BTreeMap::new(), } } + + /// Convert these build parameters to their stored equivalent + pub fn to_stored(mut self) -> Option { + let mut stored = StoredBuildParams::default(); + // Store the spends + for i in 0..self.spends.len() { + stored.spend_params.push(self.spends.remove(&i)?); + } + // Store the converts + for i in 0..self.converts.len() { + stored.convert_params.push(self.converts.remove(&i)?); + } + // Store the outputs + for i in 0..self.outputs.len() { + stored.output_params.push(self.outputs.remove(&i)?); + } + Some(stored) + } } impl RngBuildParams {