Factor Sprout transaction elements into a separate module.

This commit is contained in:
Kris Nuttycombe 2021-03-04 08:27:39 -07:00 committed by Jack Grigg
parent 6e371a8c13
commit 7fcb7bbe99
2 changed files with 174 additions and 167 deletions

View File

@ -23,15 +23,11 @@ use crate::{
};
pub mod amount;
pub use self::amount::Amount;
pub mod sprout;
pub use self::{amount::Amount, sprout::JSDescription};
// π_A + π_B + π_C
pub const GROTH_PROOF_SIZE: usize = 48 + 96 + 48;
// π_A + π_A' + π_B + π_B' + π_C + π_C' + π_K + π_H
const PHGR_PROOF_SIZE: usize = 33 + 33 + 65 + 33 + 33 + 33 + 33 + 33;
const ZC_NUM_JS_INPUTS: usize = 2;
const ZC_NUM_JS_OUTPUTS: usize = 2;
#[derive(Clone, Debug, PartialEq)]
pub struct OutPoint {
@ -440,164 +436,3 @@ impl OutputDescription {
writer.write_all(&self.zkproof)
}
}
#[derive(Clone)]
pub(crate) enum SproutProof {
Groth([u8; GROTH_PROOF_SIZE]),
PHGR([u8; PHGR_PROOF_SIZE]),
}
impl std::fmt::Debug for SproutProof {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
SproutProof::Groth(_) => write!(f, "SproutProof::Groth"),
SproutProof::PHGR(_) => write!(f, "SproutProof::PHGR"),
}
}
}
#[derive(Clone)]
pub struct JSDescription {
pub(crate) vpub_old: Amount,
pub(crate) vpub_new: Amount,
pub(crate) anchor: [u8; 32],
pub(crate) nullifiers: [[u8; 32]; ZC_NUM_JS_INPUTS],
pub(crate) commitments: [[u8; 32]; ZC_NUM_JS_OUTPUTS],
pub(crate) ephemeral_key: [u8; 32],
pub(crate) random_seed: [u8; 32],
pub(crate) macs: [[u8; 32]; ZC_NUM_JS_INPUTS],
pub(crate) proof: SproutProof,
pub(crate) ciphertexts: [[u8; 601]; ZC_NUM_JS_OUTPUTS],
}
impl std::fmt::Debug for JSDescription {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"JSDescription(
vpub_old = {:?}, vpub_new = {:?},
anchor = {:?},
nullifiers = {:?},
commitments = {:?},
ephemeral_key = {:?},
random_seed = {:?},
macs = {:?})",
self.vpub_old,
self.vpub_new,
self.anchor,
self.nullifiers,
self.commitments,
self.ephemeral_key,
self.random_seed,
self.macs
)
}
}
impl JSDescription {
pub fn read<R: Read>(mut reader: R, use_groth: bool) -> io::Result<Self> {
// Consensus rule (§4.3): Canonical encoding is enforced here
let vpub_old = {
let mut tmp = [0u8; 8];
reader.read_exact(&mut tmp)?;
Amount::from_u64_le_bytes(tmp)
}
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "vpub_old out of range"))?;
// Consensus rule (§4.3): Canonical encoding is enforced here
let vpub_new = {
let mut tmp = [0u8; 8];
reader.read_exact(&mut tmp)?;
Amount::from_u64_le_bytes(tmp)
}
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "vpub_new out of range"))?;
// Consensus rule (§4.3): One of vpub_old and vpub_new being zero is
// enforced by CheckTransactionWithoutProofVerification() in zcashd.
let mut anchor = [0u8; 32];
reader.read_exact(&mut anchor)?;
let mut nullifiers = [[0u8; 32]; ZC_NUM_JS_INPUTS];
nullifiers
.iter_mut()
.map(|nf| reader.read_exact(nf))
.collect::<io::Result<()>>()?;
let mut commitments = [[0u8; 32]; ZC_NUM_JS_OUTPUTS];
commitments
.iter_mut()
.map(|cm| reader.read_exact(cm))
.collect::<io::Result<()>>()?;
// Consensus rule (§4.3): Canonical encoding is enforced by
// ZCNoteDecryption::decrypt() in zcashd
let mut ephemeral_key = [0u8; 32];
reader.read_exact(&mut ephemeral_key)?;
let mut random_seed = [0u8; 32];
reader.read_exact(&mut random_seed)?;
let mut macs = [[0u8; 32]; ZC_NUM_JS_INPUTS];
macs.iter_mut()
.map(|mac| reader.read_exact(mac))
.collect::<io::Result<()>>()?;
let proof = if use_groth {
// Consensus rules (§4.3):
// - Canonical encoding is enforced in librustzcash_sprout_verify()
// - Proof validity is enforced in librustzcash_sprout_verify()
let mut proof = [0u8; GROTH_PROOF_SIZE];
reader.read_exact(&mut proof)?;
SproutProof::Groth(proof)
} else {
// Consensus rules (§4.3):
// - Canonical encoding is enforced by PHGRProof in zcashd
// - Proof validity is enforced by JSDescription::Verify() in zcashd
let mut proof = [0u8; PHGR_PROOF_SIZE];
reader.read_exact(&mut proof)?;
SproutProof::PHGR(proof)
};
let mut ciphertexts = [[0u8; 601]; ZC_NUM_JS_OUTPUTS];
ciphertexts
.iter_mut()
.map(|ct| reader.read_exact(ct))
.collect::<io::Result<()>>()?;
Ok(JSDescription {
vpub_old,
vpub_new,
anchor,
nullifiers,
commitments,
ephemeral_key,
random_seed,
macs,
proof,
ciphertexts,
})
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_all(&self.vpub_old.to_i64_le_bytes())?;
writer.write_all(&self.vpub_new.to_i64_le_bytes())?;
writer.write_all(&self.anchor)?;
writer.write_all(&self.nullifiers[0])?;
writer.write_all(&self.nullifiers[1])?;
writer.write_all(&self.commitments[0])?;
writer.write_all(&self.commitments[1])?;
writer.write_all(&self.ephemeral_key)?;
writer.write_all(&self.random_seed)?;
writer.write_all(&self.macs[0])?;
writer.write_all(&self.macs[1])?;
match &self.proof {
SproutProof::Groth(p) => writer.write_all(p)?,
SproutProof::PHGR(p) => writer.write_all(p)?,
}
writer.write_all(&self.ciphertexts[0])?;
writer.write_all(&self.ciphertexts[1])
}
}

View File

@ -0,0 +1,172 @@
//! Structs representing the components within Zcash transactions.
use std::io::{self, Read, Write};
use super::{amount::Amount, GROTH_PROOF_SIZE};
// π_A + π_A' + π_B + π_B' + π_C + π_C' + π_K + π_H
const PHGR_PROOF_SIZE: usize = 33 + 33 + 65 + 33 + 33 + 33 + 33 + 33;
const ZC_NUM_JS_INPUTS: usize = 2;
const ZC_NUM_JS_OUTPUTS: usize = 2;
#[derive(Clone)]
pub(crate) enum SproutProof {
Groth([u8; GROTH_PROOF_SIZE]),
PHGR([u8; PHGR_PROOF_SIZE]),
}
impl std::fmt::Debug for SproutProof {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
SproutProof::Groth(_) => write!(f, "SproutProof::Groth"),
SproutProof::PHGR(_) => write!(f, "SproutProof::PHGR"),
}
}
}
#[derive(Clone)]
pub struct JSDescription {
pub(crate) vpub_old: Amount,
pub(crate) vpub_new: Amount,
pub(crate) anchor: [u8; 32],
pub(crate) nullifiers: [[u8; 32]; ZC_NUM_JS_INPUTS],
pub(crate) commitments: [[u8; 32]; ZC_NUM_JS_OUTPUTS],
pub(crate) ephemeral_key: [u8; 32],
pub(crate) random_seed: [u8; 32],
pub(crate) macs: [[u8; 32]; ZC_NUM_JS_INPUTS],
pub(crate) proof: SproutProof,
pub(crate) ciphertexts: [[u8; 601]; ZC_NUM_JS_OUTPUTS],
}
impl std::fmt::Debug for JSDescription {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"JSDescription(
vpub_old = {:?}, vpub_new = {:?},
anchor = {:?},
nullifiers = {:?},
commitments = {:?},
ephemeral_key = {:?},
random_seed = {:?},
macs = {:?})",
self.vpub_old,
self.vpub_new,
self.anchor,
self.nullifiers,
self.commitments,
self.ephemeral_key,
self.random_seed,
self.macs
)
}
}
impl JSDescription {
pub fn read<R: Read>(mut reader: R, use_groth: bool) -> io::Result<Self> {
// Consensus rule (§4.3): Canonical encoding is enforced here
let vpub_old = {
let mut tmp = [0u8; 8];
reader.read_exact(&mut tmp)?;
Amount::from_u64_le_bytes(tmp)
}
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "vpub_old out of range"))?;
// Consensus rule (§4.3): Canonical encoding is enforced here
let vpub_new = {
let mut tmp = [0u8; 8];
reader.read_exact(&mut tmp)?;
Amount::from_u64_le_bytes(tmp)
}
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "vpub_new out of range"))?;
// Consensus rule (§4.3): One of vpub_old and vpub_new being zero is
// enforced by CheckTransactionWithoutProofVerification() in zcashd.
let mut anchor = [0u8; 32];
reader.read_exact(&mut anchor)?;
let mut nullifiers = [[0u8; 32]; ZC_NUM_JS_INPUTS];
nullifiers
.iter_mut()
.map(|nf| reader.read_exact(nf))
.collect::<io::Result<()>>()?;
let mut commitments = [[0u8; 32]; ZC_NUM_JS_OUTPUTS];
commitments
.iter_mut()
.map(|cm| reader.read_exact(cm))
.collect::<io::Result<()>>()?;
// Consensus rule (§4.3): Canonical encoding is enforced by
// ZCNoteDecryption::decrypt() in zcashd
let mut ephemeral_key = [0u8; 32];
reader.read_exact(&mut ephemeral_key)?;
let mut random_seed = [0u8; 32];
reader.read_exact(&mut random_seed)?;
let mut macs = [[0u8; 32]; ZC_NUM_JS_INPUTS];
macs.iter_mut()
.map(|mac| reader.read_exact(mac))
.collect::<io::Result<()>>()?;
let proof = if use_groth {
// Consensus rules (§4.3):
// - Canonical encoding is enforced in librustzcash_sprout_verify()
// - Proof validity is enforced in librustzcash_sprout_verify()
let mut proof = [0u8; GROTH_PROOF_SIZE];
reader.read_exact(&mut proof)?;
SproutProof::Groth(proof)
} else {
// Consensus rules (§4.3):
// - Canonical encoding is enforced by PHGRProof in zcashd
// - Proof validity is enforced by JSDescription::Verify() in zcashd
let mut proof = [0u8; PHGR_PROOF_SIZE];
reader.read_exact(&mut proof)?;
SproutProof::PHGR(proof)
};
let mut ciphertexts = [[0u8; 601]; ZC_NUM_JS_OUTPUTS];
ciphertexts
.iter_mut()
.map(|ct| reader.read_exact(ct))
.collect::<io::Result<()>>()?;
Ok(JSDescription {
vpub_old,
vpub_new,
anchor,
nullifiers,
commitments,
ephemeral_key,
random_seed,
macs,
proof,
ciphertexts,
})
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_all(&self.vpub_old.to_i64_le_bytes())?;
writer.write_all(&self.vpub_new.to_i64_le_bytes())?;
writer.write_all(&self.anchor)?;
writer.write_all(&self.nullifiers[0])?;
writer.write_all(&self.nullifiers[1])?;
writer.write_all(&self.commitments[0])?;
writer.write_all(&self.commitments[1])?;
writer.write_all(&self.ephemeral_key)?;
writer.write_all(&self.random_seed)?;
writer.write_all(&self.macs[0])?;
writer.write_all(&self.macs[1])?;
match &self.proof {
SproutProof::Groth(p) => writer.write_all(p)?,
SproutProof::PHGR(p) => writer.write_all(p)?,
}
writer.write_all(&self.ciphertexts[0])?;
writer.write_all(&self.ciphertexts[1])
}
}