Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the serialization of transaction build parameters. #84

Merged
merged 1 commit into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion masp_primitives/src/sapling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ impl ValueCommitment {
}
}

#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct ProofGenerationKey {
pub ak: jubjub::SubgroupPoint,
pub nsk: jubjub::Fr,
Expand All @@ -211,6 +211,54 @@ impl ProofGenerationKey {
}
}

impl BorshSerialize for ProofGenerationKey {
fn serialize<W: Write>(&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<R: Read>(reader: &mut R) -> io::Result<Self> {
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<Declaration, Definition>) {
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);
Expand Down
174 changes: 170 additions & 4 deletions masp_primitives/src/transaction/components/sapling/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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,
Expand All @@ -75,15 +76,113 @@ pub struct SpendBuildParams {
proof_generation_key: Option<ProofGenerationKey>,
}

impl BorshSerialize for SpendBuildParams {
fn serialize<W: std::io::Write>(&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<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
// 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::<Signature>::deserialize_reader(reader)?;
// Read the proof generation key
let proof_generation_key = Option::<ProofGenerationKey>::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<Declaration, Definition>) {
let definition = Definition::Struct {
fields: Fields::NamedFields(vec![
("rcv".into(), <[u8; 32]>::declaration()),
("alpha".into(), <[u8; 32]>::declaration()),
("auth_sig".into(), Option::<Signature>::declaration()),
(
"proof_generation_key".into(),
Option::<ProofGenerationKey>::declaration(),
),
]),
};
add_definition(Self::declaration(), definition, definitions);
<[u8; 32]>::add_definitions_recursively(definitions);
Option::<Signature>::add_definitions_recursively(definitions);
Option::<ProofGenerationKey>::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<W: std::io::Write>(&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<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
// 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<Declaration, Definition>) {
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,
Expand All @@ -93,8 +192,57 @@ pub struct OutputBuildParams {
rseed: [u8; 32],
}

impl BorshSerialize for OutputBuildParams {
fn serialize<W: std::io::Write>(&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<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
// 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<Declaration, Definition>) {
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<SpendBuildParams>,
Expand Down Expand Up @@ -160,6 +308,24 @@ impl<R: CryptoRng + RngCore> RngBuildParams<R> {
outputs: BTreeMap::new(),
}
}

/// Convert these build parameters to their stored equivalent
pub fn to_stored(mut self) -> Option<StoredBuildParams> {
let mut stored = StoredBuildParams::default();
// Store the spends
for i in 0..self.spends.len() {
batconjurer marked this conversation as resolved.
Show resolved Hide resolved
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<R: CryptoRng + RngCore> RngBuildParams<R> {
Expand Down
Loading