Merge pull request #348 from nuttycom/refactor/component_modules
Split transaction components into separate modules.
This commit is contained in:
commit
d03a19731f
|
@ -26,7 +26,7 @@ use blake2b_simd::Params;
|
|||
|
||||
use zcash_primitives::{
|
||||
extensions::transparent::{Extension, ExtensionTxBuilder, FromPayload, ToPayload},
|
||||
transaction::components::{amount::Amount, OutPoint, TzeOut},
|
||||
transaction::components::{amount::Amount, TzeOut, TzeOutPoint},
|
||||
};
|
||||
|
||||
/// Types and constants used for Mode 0 (open a channel)
|
||||
|
@ -374,7 +374,7 @@ impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<&mut B> {
|
|||
/// precondition to the transaction under construction.
|
||||
pub fn demo_transfer_to_close(
|
||||
&mut self,
|
||||
prevout: (OutPoint, TzeOut),
|
||||
prevout: (TzeOutPoint, TzeOut),
|
||||
transfer_amount: Amount,
|
||||
preimage_1: [u8; 32],
|
||||
hash_2: [u8; 32],
|
||||
|
@ -416,7 +416,7 @@ impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<&mut B> {
|
|||
/// Add a channel-closing witness to the transaction under construction.
|
||||
pub fn demo_close(
|
||||
&mut self,
|
||||
prevout: (OutPoint, TzeOut),
|
||||
prevout: (TzeOutPoint, TzeOut),
|
||||
preimage_2: [u8; 32],
|
||||
) -> Result<(), DemoBuildError<B::BuildError>> {
|
||||
let hash_2 = {
|
||||
|
@ -477,7 +477,7 @@ mod tests {
|
|||
builder::Builder,
|
||||
components::{
|
||||
amount::{Amount, DEFAULT_FEE},
|
||||
OutPoint, TzeIn, TzeOut,
|
||||
TzeIn, TzeOut, TzeOutPoint,
|
||||
},
|
||||
Transaction, TransactionData,
|
||||
},
|
||||
|
@ -625,7 +625,7 @@ mod tests {
|
|||
//
|
||||
|
||||
let in_b = TzeIn {
|
||||
prevout: OutPoint::new(tx_a.txid().0, 0),
|
||||
prevout: TzeOutPoint::new(tx_a.txid().0, 0),
|
||||
witness: tze::Witness::from(0, &Witness::open(preimage_1)),
|
||||
};
|
||||
let out_b = TzeOut {
|
||||
|
@ -642,7 +642,7 @@ mod tests {
|
|||
//
|
||||
|
||||
let in_c = TzeIn {
|
||||
prevout: OutPoint::new(tx_b.txid().0, 0),
|
||||
prevout: TzeOutPoint::new(tx_b.txid().0, 0),
|
||||
witness: tze::Witness::from(0, &Witness::close(preimage_2)),
|
||||
};
|
||||
|
||||
|
@ -736,7 +736,10 @@ mod tests {
|
|||
txn_builder: &mut builder_b,
|
||||
extension_id: 0,
|
||||
};
|
||||
let prevout_a = (OutPoint::new(tx_a.txid().0, 0), tx_a.tze_outputs[0].clone());
|
||||
let prevout_a = (
|
||||
TzeOutPoint::new(tx_a.txid().0, 0),
|
||||
tx_a.tze_outputs[0].clone(),
|
||||
);
|
||||
let value_xfr = value - DEFAULT_FEE;
|
||||
db_b.demo_transfer_to_close(prevout_a, value_xfr, preimage_1, h2)
|
||||
.map_err(|e| format!("transfer failure: {:?}", e))
|
||||
|
@ -755,7 +758,10 @@ mod tests {
|
|||
txn_builder: &mut builder_c,
|
||||
extension_id: 0,
|
||||
};
|
||||
let prevout_b = (OutPoint::new(tx_a.txid().0, 0), tx_b.tze_outputs[0].clone());
|
||||
let prevout_b = (
|
||||
TzeOutPoint::new(tx_a.txid().0, 0),
|
||||
tx_b.tze_outputs[0].clone(),
|
||||
);
|
||||
db_c.demo_close(prevout_b, preimage_2)
|
||||
.map_err(|e| format!("close failure: {:?}", e))
|
||||
.unwrap();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Core traits and structs for Transparent Zcash Extensions.
|
||||
|
||||
use crate::transaction::components::{Amount, OutPoint, TzeOut};
|
||||
use crate::transaction::components::{Amount, TzeOut, TzeOutPoint};
|
||||
use std::fmt;
|
||||
|
||||
/// Binary parsing capability for TZE preconditions & witnesses.
|
||||
|
@ -178,7 +178,7 @@ pub trait ExtensionTxBuilder<'a> {
|
|||
&mut self,
|
||||
extension_id: u32,
|
||||
mode: u32,
|
||||
prevout: (OutPoint, TzeOut),
|
||||
prevout: (TzeOutPoint, TzeOut),
|
||||
witness_builder: WBuilder,
|
||||
) -> Result<(), Self::BuildError>
|
||||
where
|
||||
|
|
|
@ -32,17 +32,17 @@ use crate::{
|
|||
};
|
||||
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
use crate::{legacy::Script, transaction::components::TxIn};
|
||||
use crate::{
|
||||
legacy::Script,
|
||||
transaction::components::{OutPoint, TxIn},
|
||||
};
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
use crate::{
|
||||
extensions::transparent::{self as tze, ExtensionTxBuilder, ToPayload},
|
||||
transaction::components::{TzeIn, TzeOut},
|
||||
transaction::components::{TzeIn, TzeOut, TzeOutPoint},
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "transparent-inputs", feature = "zfuture"))]
|
||||
use crate::transaction::components::OutPoint;
|
||||
|
||||
#[cfg(any(test, feature = "test-dependencies"))]
|
||||
use crate::prover::mock::MockTxProver;
|
||||
|
||||
|
@ -869,7 +869,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> ExtensionTxBuilder<'a
|
|||
&mut self,
|
||||
extension_id: u32,
|
||||
mode: u32,
|
||||
(outpoint, prevout): (OutPoint, TzeOut),
|
||||
(outpoint, prevout): (TzeOutPoint, TzeOut),
|
||||
witness_builder: WBuilder,
|
||||
) -> Result<(), Self::BuildError>
|
||||
where
|
||||
|
|
|
@ -1,603 +1,19 @@
|
|||
//! Structs representing the components within Zcash transactions.
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
use ff::PrimeField;
|
||||
use group::GroupEncoding;
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::{
|
||||
legacy::Script,
|
||||
primitives::Nullifier,
|
||||
redjubjub::{PublicKey, Signature},
|
||||
};
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
use crate::{
|
||||
extensions::transparent as tze,
|
||||
serialize::{CompactSize, Vector},
|
||||
};
|
||||
|
||||
pub mod amount;
|
||||
pub use self::amount::Amount;
|
||||
pub mod sapling;
|
||||
pub mod sprout;
|
||||
pub mod transparent;
|
||||
pub mod tze;
|
||||
pub use self::{
|
||||
amount::Amount,
|
||||
sapling::{OutputDescription, SpendDescription},
|
||||
sprout::JSDescription,
|
||||
transparent::{OutPoint, TxIn, TxOut},
|
||||
};
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
pub use self::tze::{TzeIn, TzeOut, TzeOutPoint};
|
||||
|
||||
// π_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 {
|
||||
hash: [u8; 32],
|
||||
n: u32,
|
||||
}
|
||||
|
||||
impl OutPoint {
|
||||
pub fn new(hash: [u8; 32], n: u32) -> Self {
|
||||
OutPoint { hash, n }
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||
let mut hash = [0u8; 32];
|
||||
reader.read_exact(&mut hash)?;
|
||||
let n = reader.read_u32::<LittleEndian>()?;
|
||||
Ok(OutPoint { hash, n })
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(&self.hash)?;
|
||||
writer.write_u32::<LittleEndian>(self.n)
|
||||
}
|
||||
|
||||
pub fn n(&self) -> u32 {
|
||||
self.n
|
||||
}
|
||||
|
||||
pub fn hash(&self) -> &[u8; 32] {
|
||||
&self.hash
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TxIn {
|
||||
pub prevout: OutPoint,
|
||||
pub script_sig: Script,
|
||||
pub sequence: u32,
|
||||
}
|
||||
|
||||
impl TxIn {
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "transparent-inputs")))]
|
||||
pub fn new(prevout: OutPoint) -> Self {
|
||||
TxIn {
|
||||
prevout,
|
||||
script_sig: Script::default(),
|
||||
sequence: std::u32::MAX,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
|
||||
let prevout = OutPoint::read(&mut reader)?;
|
||||
let script_sig = Script::read(&mut reader)?;
|
||||
let sequence = reader.read_u32::<LittleEndian>()?;
|
||||
|
||||
Ok(TxIn {
|
||||
prevout,
|
||||
script_sig,
|
||||
sequence,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
self.prevout.write(&mut writer)?;
|
||||
self.script_sig.write(&mut writer)?;
|
||||
writer.write_u32::<LittleEndian>(self.sequence)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TxOut {
|
||||
pub value: Amount,
|
||||
pub script_pubkey: Script,
|
||||
}
|
||||
|
||||
impl TxOut {
|
||||
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
|
||||
let value = {
|
||||
let mut tmp = [0u8; 8];
|
||||
reader.read_exact(&mut tmp)?;
|
||||
Amount::from_nonnegative_i64_le_bytes(tmp)
|
||||
}
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "value out of range"))?;
|
||||
let script_pubkey = Script::read(&mut reader)?;
|
||||
|
||||
Ok(TxOut {
|
||||
value,
|
||||
script_pubkey,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(&self.value.to_i64_le_bytes())?;
|
||||
self.script_pubkey.write(&mut writer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
fn to_io_error(_: std::num::TryFromIntError) -> io::Error {
|
||||
io::Error::new(io::ErrorKind::InvalidData, "value out of range")
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg(feature = "zfuture")]
|
||||
pub struct TzeIn {
|
||||
pub prevout: OutPoint,
|
||||
pub witness: tze::Witness,
|
||||
}
|
||||
|
||||
/// Transaction encoding and decoding functions conforming to [ZIP 222].
|
||||
///
|
||||
/// [ZIP 222]: https://zips.z.cash/zip-0222#encoding-in-transactions
|
||||
#[cfg(feature = "zfuture")]
|
||||
impl TzeIn {
|
||||
/// Convenience constructor
|
||||
pub fn new(prevout: OutPoint, extension_id: u32, mode: u32) -> Self {
|
||||
TzeIn {
|
||||
prevout,
|
||||
witness: tze::Witness {
|
||||
extension_id,
|
||||
mode,
|
||||
payload: vec![],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Read witness metadata & payload
|
||||
///
|
||||
/// Used to decode the encoded form used within a serialized
|
||||
/// transaction.
|
||||
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
|
||||
let prevout = OutPoint::read(&mut reader)?;
|
||||
|
||||
let extension_id = CompactSize::read(&mut reader)?;
|
||||
let mode = CompactSize::read(&mut reader)?;
|
||||
let payload = Vector::read(&mut reader, |r| r.read_u8())?;
|
||||
|
||||
Ok(TzeIn {
|
||||
prevout,
|
||||
witness: tze::Witness {
|
||||
extension_id: u32::try_from(extension_id).map_err(to_io_error)?,
|
||||
mode: u32::try_from(mode).map_err(to_io_error)?,
|
||||
payload,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Write without witness data (for signature hashing)
|
||||
///
|
||||
/// This is also used as the prefix for the encoded form used
|
||||
/// within a serialized transaction.
|
||||
pub fn write_without_witness<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
self.prevout.write(&mut writer)?;
|
||||
|
||||
CompactSize::write(
|
||||
&mut writer,
|
||||
usize::try_from(self.witness.extension_id).map_err(to_io_error)?,
|
||||
)?;
|
||||
|
||||
CompactSize::write(
|
||||
&mut writer,
|
||||
usize::try_from(self.witness.mode).map_err(to_io_error)?,
|
||||
)
|
||||
}
|
||||
|
||||
/// Write prevout, extension, and mode followed by witness data.
|
||||
///
|
||||
/// This calls [`write_without_witness`] to serialize witness metadata,
|
||||
/// then appends the witness bytes themselves. This is the encoded
|
||||
/// form that is used in a serialized transaction.
|
||||
///
|
||||
/// [`write_without_witness`]: TzeIn::write_without_witness
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
self.write_without_witness(&mut writer)?;
|
||||
Vector::write(&mut writer, &self.witness.payload, |w, b| w.write_u8(*b))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg(feature = "zfuture")]
|
||||
pub struct TzeOut {
|
||||
pub value: Amount,
|
||||
pub precondition: tze::Precondition,
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
impl TzeOut {
|
||||
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
|
||||
let value = {
|
||||
let mut tmp = [0; 8];
|
||||
reader.read_exact(&mut tmp)?;
|
||||
Amount::from_nonnegative_i64_le_bytes(tmp)
|
||||
}
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "value out of range"))?;
|
||||
|
||||
let extension_id = CompactSize::read(&mut reader)?;
|
||||
let mode = CompactSize::read(&mut reader)?;
|
||||
let payload = Vector::read(&mut reader, |r| r.read_u8())?;
|
||||
|
||||
Ok(TzeOut {
|
||||
value,
|
||||
precondition: tze::Precondition {
|
||||
extension_id: u32::try_from(extension_id).map_err(to_io_error)?,
|
||||
mode: u32::try_from(mode).map_err(to_io_error)?,
|
||||
payload,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(&self.value.to_i64_le_bytes())?;
|
||||
|
||||
CompactSize::write(
|
||||
&mut writer,
|
||||
usize::try_from(self.precondition.extension_id).map_err(to_io_error)?,
|
||||
)?;
|
||||
CompactSize::write(
|
||||
&mut writer,
|
||||
usize::try_from(self.precondition.mode).map_err(to_io_error)?,
|
||||
)?;
|
||||
Vector::write(&mut writer, &self.precondition.payload, |w, b| {
|
||||
w.write_u8(*b)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SpendDescription {
|
||||
pub cv: jubjub::ExtendedPoint,
|
||||
pub anchor: bls12_381::Scalar,
|
||||
pub nullifier: Nullifier,
|
||||
pub rk: PublicKey,
|
||||
pub zkproof: [u8; GROTH_PROOF_SIZE],
|
||||
pub spend_auth_sig: Option<Signature>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SpendDescription {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(
|
||||
f,
|
||||
"SpendDescription(cv = {:?}, anchor = {:?}, nullifier = {:?}, rk = {:?}, spend_auth_sig = {:?})",
|
||||
self.cv, self.anchor, self.nullifier, self.rk, self.spend_auth_sig
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl SpendDescription {
|
||||
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
|
||||
// Consensus rules (§4.4):
|
||||
// - Canonical encoding is enforced here.
|
||||
// - "Not small order" is enforced in SaplingVerificationContext::check_spend()
|
||||
// (located in zcash_proofs::sapling::verifier).
|
||||
let cv = {
|
||||
let mut bytes = [0u8; 32];
|
||||
reader.read_exact(&mut bytes)?;
|
||||
let cv = jubjub::ExtendedPoint::from_bytes(&bytes);
|
||||
if cv.is_none().into() {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid cv"));
|
||||
}
|
||||
cv.unwrap()
|
||||
};
|
||||
|
||||
// Consensus rule (§7.3): Canonical encoding is enforced here
|
||||
let anchor = {
|
||||
let mut f = [0u8; 32];
|
||||
reader.read_exact(&mut f)?;
|
||||
bls12_381::Scalar::from_repr(f)
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "anchor not in field"))?
|
||||
};
|
||||
|
||||
let mut nullifier = Nullifier([0u8; 32]);
|
||||
reader.read_exact(&mut nullifier.0)?;
|
||||
|
||||
// Consensus rules (§4.4):
|
||||
// - Canonical encoding is enforced here.
|
||||
// - "Not small order" is enforced in SaplingVerificationContext::check_spend()
|
||||
let rk = PublicKey::read(&mut reader)?;
|
||||
|
||||
// Consensus rules (§4.4):
|
||||
// - Canonical encoding is enforced by the API of SaplingVerificationContext::check_spend()
|
||||
// due to the need to parse this into a bellman::groth16::Proof.
|
||||
// - Proof validity is enforced in SaplingVerificationContext::check_spend()
|
||||
let mut zkproof = [0u8; GROTH_PROOF_SIZE];
|
||||
reader.read_exact(&mut zkproof)?;
|
||||
|
||||
// Consensus rules (§4.4):
|
||||
// - Canonical encoding is enforced here.
|
||||
// - Signature validity is enforced in SaplingVerificationContext::check_spend()
|
||||
let spend_auth_sig = Some(Signature::read(&mut reader)?);
|
||||
|
||||
Ok(SpendDescription {
|
||||
cv,
|
||||
anchor,
|
||||
nullifier,
|
||||
rk,
|
||||
zkproof,
|
||||
spend_auth_sig,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(&self.cv.to_bytes())?;
|
||||
writer.write_all(self.anchor.to_repr().as_ref())?;
|
||||
writer.write_all(&self.nullifier.0)?;
|
||||
self.rk.write(&mut writer)?;
|
||||
writer.write_all(&self.zkproof)?;
|
||||
match self.spend_auth_sig {
|
||||
Some(sig) => sig.write(&mut writer),
|
||||
None => Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Missing spend auth signature",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OutputDescription {
|
||||
pub cv: jubjub::ExtendedPoint,
|
||||
pub cmu: bls12_381::Scalar,
|
||||
pub ephemeral_key: jubjub::ExtendedPoint,
|
||||
pub enc_ciphertext: [u8; 580],
|
||||
pub out_ciphertext: [u8; 80],
|
||||
pub zkproof: [u8; GROTH_PROOF_SIZE],
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for OutputDescription {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(
|
||||
f,
|
||||
"OutputDescription(cv = {:?}, cmu = {:?}, ephemeral_key = {:?})",
|
||||
self.cv, self.cmu, self.ephemeral_key
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputDescription {
|
||||
pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
|
||||
// Consensus rules (§4.5):
|
||||
// - Canonical encoding is enforced here.
|
||||
// - "Not small order" is enforced in SaplingVerificationContext::check_output()
|
||||
// (located in zcash_proofs::sapling::verifier).
|
||||
let cv = {
|
||||
let mut bytes = [0u8; 32];
|
||||
reader.read_exact(&mut bytes)?;
|
||||
let cv = jubjub::ExtendedPoint::from_bytes(&bytes);
|
||||
if cv.is_none().into() {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid cv"));
|
||||
}
|
||||
cv.unwrap()
|
||||
};
|
||||
|
||||
// Consensus rule (§7.4): Canonical encoding is enforced here
|
||||
let cmu = {
|
||||
let mut f = [0u8; 32];
|
||||
reader.read_exact(&mut f)?;
|
||||
bls12_381::Scalar::from_repr(f)
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "cmu not in field"))?
|
||||
};
|
||||
|
||||
// Consensus rules (§4.5):
|
||||
// - Canonical encoding is enforced here.
|
||||
// - "Not small order" is enforced in SaplingVerificationContext::check_output()
|
||||
let ephemeral_key = {
|
||||
let mut bytes = [0u8; 32];
|
||||
reader.read_exact(&mut bytes)?;
|
||||
let ephemeral_key = jubjub::ExtendedPoint::from_bytes(&bytes);
|
||||
if ephemeral_key.is_none().into() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"invalid ephemeral_key",
|
||||
));
|
||||
}
|
||||
ephemeral_key.unwrap()
|
||||
};
|
||||
|
||||
let mut enc_ciphertext = [0u8; 580];
|
||||
let mut out_ciphertext = [0u8; 80];
|
||||
reader.read_exact(&mut enc_ciphertext)?;
|
||||
reader.read_exact(&mut out_ciphertext)?;
|
||||
|
||||
// Consensus rules (§4.5):
|
||||
// - Canonical encoding is enforced by the API of SaplingVerificationContext::check_output()
|
||||
// due to the need to parse this into a bellman::groth16::Proof.
|
||||
// - Proof validity is enforced in SaplingVerificationContext::check_output()
|
||||
let mut zkproof = [0u8; GROTH_PROOF_SIZE];
|
||||
reader.read_exact(&mut zkproof)?;
|
||||
|
||||
Ok(OutputDescription {
|
||||
cv,
|
||||
cmu,
|
||||
ephemeral_key,
|
||||
enc_ciphertext,
|
||||
out_ciphertext,
|
||||
zkproof,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(&self.cv.to_bytes())?;
|
||||
writer.write_all(self.cmu.to_repr().as_ref())?;
|
||||
writer.write_all(&self.ephemeral_key.to_bytes())?;
|
||||
writer.write_all(&self.enc_ciphertext)?;
|
||||
writer.write_all(&self.out_ciphertext)?;
|
||||
writer.write_all(&self.zkproof)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
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 {
|
||||
vpub_old: Amount,
|
||||
vpub_new: Amount,
|
||||
anchor: [u8; 32],
|
||||
nullifiers: [[u8; 32]; ZC_NUM_JS_INPUTS],
|
||||
commitments: [[u8; 32]; ZC_NUM_JS_OUTPUTS],
|
||||
ephemeral_key: [u8; 32],
|
||||
random_seed: [u8; 32],
|
||||
macs: [[u8; 32]; ZC_NUM_JS_INPUTS],
|
||||
proof: SproutProof,
|
||||
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])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
use ff::PrimeField;
|
||||
use group::GroupEncoding;
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use crate::{
|
||||
primitives::Nullifier,
|
||||
redjubjub::{PublicKey, Signature},
|
||||
};
|
||||
|
||||
use super::GROTH_PROOF_SIZE;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SpendDescription {
|
||||
pub cv: jubjub::ExtendedPoint,
|
||||
pub anchor: bls12_381::Scalar,
|
||||
pub nullifier: Nullifier,
|
||||
pub rk: PublicKey,
|
||||
pub zkproof: [u8; GROTH_PROOF_SIZE],
|
||||
pub spend_auth_sig: Option<Signature>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SpendDescription {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(
|
||||
f,
|
||||
"SpendDescription(cv = {:?}, anchor = {:?}, nullifier = {:?}, rk = {:?}, spend_auth_sig = {:?})",
|
||||
self.cv, self.anchor, self.nullifier, self.rk, self.spend_auth_sig
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl SpendDescription {
|
||||
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
|
||||
// Consensus rules (§4.4):
|
||||
// - Canonical encoding is enforced here.
|
||||
// - "Not small order" is enforced in SaplingVerificationContext::check_spend()
|
||||
// (located in zcash_proofs::sapling::verifier).
|
||||
let cv = {
|
||||
let mut bytes = [0u8; 32];
|
||||
reader.read_exact(&mut bytes)?;
|
||||
let cv = jubjub::ExtendedPoint::from_bytes(&bytes);
|
||||
if cv.is_none().into() {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid cv"));
|
||||
}
|
||||
cv.unwrap()
|
||||
};
|
||||
|
||||
// Consensus rule (§7.3): Canonical encoding is enforced here
|
||||
let anchor = {
|
||||
let mut f = [0u8; 32];
|
||||
reader.read_exact(&mut f)?;
|
||||
bls12_381::Scalar::from_repr(f)
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "anchor not in field"))?
|
||||
};
|
||||
|
||||
let mut nullifier = Nullifier([0u8; 32]);
|
||||
reader.read_exact(&mut nullifier.0)?;
|
||||
|
||||
// Consensus rules (§4.4):
|
||||
// - Canonical encoding is enforced here.
|
||||
// - "Not small order" is enforced in SaplingVerificationContext::check_spend()
|
||||
let rk = PublicKey::read(&mut reader)?;
|
||||
|
||||
// Consensus rules (§4.4):
|
||||
// - Canonical encoding is enforced by the API of SaplingVerificationContext::check_spend()
|
||||
// due to the need to parse this into a bellman::groth16::Proof.
|
||||
// - Proof validity is enforced in SaplingVerificationContext::check_spend()
|
||||
let mut zkproof = [0u8; GROTH_PROOF_SIZE];
|
||||
reader.read_exact(&mut zkproof)?;
|
||||
|
||||
// Consensus rules (§4.4):
|
||||
// - Canonical encoding is enforced here.
|
||||
// - Signature validity is enforced in SaplingVerificationContext::check_spend()
|
||||
let spend_auth_sig = Some(Signature::read(&mut reader)?);
|
||||
|
||||
Ok(SpendDescription {
|
||||
cv,
|
||||
anchor,
|
||||
nullifier,
|
||||
rk,
|
||||
zkproof,
|
||||
spend_auth_sig,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(&self.cv.to_bytes())?;
|
||||
writer.write_all(self.anchor.to_repr().as_ref())?;
|
||||
writer.write_all(&self.nullifier.0)?;
|
||||
self.rk.write(&mut writer)?;
|
||||
writer.write_all(&self.zkproof)?;
|
||||
match self.spend_auth_sig {
|
||||
Some(sig) => sig.write(&mut writer),
|
||||
None => Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Missing spend auth signature",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OutputDescription {
|
||||
pub cv: jubjub::ExtendedPoint,
|
||||
pub cmu: bls12_381::Scalar,
|
||||
pub ephemeral_key: jubjub::ExtendedPoint,
|
||||
pub enc_ciphertext: [u8; 580],
|
||||
pub out_ciphertext: [u8; 80],
|
||||
pub zkproof: [u8; GROTH_PROOF_SIZE],
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for OutputDescription {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(
|
||||
f,
|
||||
"OutputDescription(cv = {:?}, cmu = {:?}, ephemeral_key = {:?})",
|
||||
self.cv, self.cmu, self.ephemeral_key
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputDescription {
|
||||
pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
|
||||
// Consensus rules (§4.5):
|
||||
// - Canonical encoding is enforced here.
|
||||
// - "Not small order" is enforced in SaplingVerificationContext::check_output()
|
||||
// (located in zcash_proofs::sapling::verifier).
|
||||
let cv = {
|
||||
let mut bytes = [0u8; 32];
|
||||
reader.read_exact(&mut bytes)?;
|
||||
let cv = jubjub::ExtendedPoint::from_bytes(&bytes);
|
||||
if cv.is_none().into() {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid cv"));
|
||||
}
|
||||
cv.unwrap()
|
||||
};
|
||||
|
||||
// Consensus rule (§7.4): Canonical encoding is enforced here
|
||||
let cmu = {
|
||||
let mut f = [0u8; 32];
|
||||
reader.read_exact(&mut f)?;
|
||||
bls12_381::Scalar::from_repr(f)
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "cmu not in field"))?
|
||||
};
|
||||
|
||||
// Consensus rules (§4.5):
|
||||
// - Canonical encoding is enforced here.
|
||||
// - "Not small order" is enforced in SaplingVerificationContext::check_output()
|
||||
let ephemeral_key = {
|
||||
let mut bytes = [0u8; 32];
|
||||
reader.read_exact(&mut bytes)?;
|
||||
let ephemeral_key = jubjub::ExtendedPoint::from_bytes(&bytes);
|
||||
if ephemeral_key.is_none().into() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"invalid ephemeral_key",
|
||||
));
|
||||
}
|
||||
ephemeral_key.unwrap()
|
||||
};
|
||||
|
||||
let mut enc_ciphertext = [0u8; 580];
|
||||
let mut out_ciphertext = [0u8; 80];
|
||||
reader.read_exact(&mut enc_ciphertext)?;
|
||||
reader.read_exact(&mut out_ciphertext)?;
|
||||
|
||||
// Consensus rules (§4.5):
|
||||
// - Canonical encoding is enforced by the API of SaplingVerificationContext::check_output()
|
||||
// due to the need to parse this into a bellman::groth16::Proof.
|
||||
// - Proof validity is enforced in SaplingVerificationContext::check_output()
|
||||
let mut zkproof = [0u8; GROTH_PROOF_SIZE];
|
||||
reader.read_exact(&mut zkproof)?;
|
||||
|
||||
Ok(OutputDescription {
|
||||
cv,
|
||||
cmu,
|
||||
ephemeral_key,
|
||||
enc_ciphertext,
|
||||
out_ciphertext,
|
||||
zkproof,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(&self.cv.to_bytes())?;
|
||||
writer.write_all(self.cmu.to_repr().as_ref())?;
|
||||
writer.write_all(&self.ephemeral_key.to_bytes())?;
|
||||
writer.write_all(&self.enc_ciphertext)?;
|
||||
writer.write_all(&self.out_ciphertext)?;
|
||||
writer.write_all(&self.zkproof)
|
||||
}
|
||||
}
|
|
@ -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])
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
//! Structs representing the components within Zcash transactions.
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use crate::legacy::Script;
|
||||
|
||||
use super::amount::Amount;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct OutPoint {
|
||||
hash: [u8; 32],
|
||||
n: u32,
|
||||
}
|
||||
|
||||
impl OutPoint {
|
||||
pub fn new(hash: [u8; 32], n: u32) -> Self {
|
||||
OutPoint { hash, n }
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||
let mut hash = [0u8; 32];
|
||||
reader.read_exact(&mut hash)?;
|
||||
let n = reader.read_u32::<LittleEndian>()?;
|
||||
Ok(OutPoint { hash, n })
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(&self.hash)?;
|
||||
writer.write_u32::<LittleEndian>(self.n)
|
||||
}
|
||||
|
||||
pub fn n(&self) -> u32 {
|
||||
self.n
|
||||
}
|
||||
|
||||
pub fn hash(&self) -> &[u8; 32] {
|
||||
&self.hash
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TxIn {
|
||||
pub prevout: OutPoint,
|
||||
pub script_sig: Script,
|
||||
pub sequence: u32,
|
||||
}
|
||||
|
||||
impl TxIn {
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "transparent-inputs")))]
|
||||
pub fn new(prevout: OutPoint) -> Self {
|
||||
TxIn {
|
||||
prevout,
|
||||
script_sig: Script::default(),
|
||||
sequence: std::u32::MAX,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
|
||||
let prevout = OutPoint::read(&mut reader)?;
|
||||
let script_sig = Script::read(&mut reader)?;
|
||||
let sequence = reader.read_u32::<LittleEndian>()?;
|
||||
|
||||
Ok(TxIn {
|
||||
prevout,
|
||||
script_sig,
|
||||
sequence,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
self.prevout.write(&mut writer)?;
|
||||
self.script_sig.write(&mut writer)?;
|
||||
writer.write_u32::<LittleEndian>(self.sequence)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TxOut {
|
||||
pub value: Amount,
|
||||
pub script_pubkey: Script,
|
||||
}
|
||||
|
||||
impl TxOut {
|
||||
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
|
||||
let value = {
|
||||
let mut tmp = [0u8; 8];
|
||||
reader.read_exact(&mut tmp)?;
|
||||
Amount::from_nonnegative_i64_le_bytes(tmp)
|
||||
}
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "value out of range"))?;
|
||||
let script_pubkey = Script::read(&mut reader)?;
|
||||
|
||||
Ok(TxOut {
|
||||
value,
|
||||
script_pubkey,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(&self.value.to_i64_le_bytes())?;
|
||||
self.script_pubkey.write(&mut writer)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
//! Structs representing the TZE components within Zcash transactions.
|
||||
#![cfg(feature = "zfuture")]
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::{
|
||||
extensions::transparent as tze,
|
||||
serialize::{CompactSize, Vector},
|
||||
};
|
||||
|
||||
use super::amount::Amount;
|
||||
|
||||
fn to_io_error(_: std::num::TryFromIntError) -> io::Error {
|
||||
io::Error::new(io::ErrorKind::InvalidData, "value out of range")
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TzeOutPoint {
|
||||
hash: [u8; 32],
|
||||
n: u32,
|
||||
}
|
||||
|
||||
impl TzeOutPoint {
|
||||
pub fn new(hash: [u8; 32], n: u32) -> Self {
|
||||
TzeOutPoint { hash, n }
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||
let mut hash = [0u8; 32];
|
||||
reader.read_exact(&mut hash)?;
|
||||
let n = reader.read_u32::<LittleEndian>()?;
|
||||
Ok(TzeOutPoint { hash, n })
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(&self.hash)?;
|
||||
writer.write_u32::<LittleEndian>(self.n)
|
||||
}
|
||||
|
||||
pub fn n(&self) -> u32 {
|
||||
self.n
|
||||
}
|
||||
|
||||
pub fn hash(&self) -> &[u8; 32] {
|
||||
&self.hash
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TzeIn {
|
||||
pub prevout: TzeOutPoint,
|
||||
pub witness: tze::Witness,
|
||||
}
|
||||
|
||||
/// Transaction encoding and decoding functions conforming to [ZIP 222].
|
||||
///
|
||||
/// [ZIP 222]: https://zips.z.cash/zip-0222#encoding-in-transactions
|
||||
impl TzeIn {
|
||||
/// Convenience constructor
|
||||
pub fn new(prevout: TzeOutPoint, extension_id: u32, mode: u32) -> Self {
|
||||
TzeIn {
|
||||
prevout,
|
||||
witness: tze::Witness {
|
||||
extension_id,
|
||||
mode,
|
||||
payload: vec![],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Read witness metadata & payload
|
||||
///
|
||||
/// Used to decode the encoded form used within a serialized
|
||||
/// transaction.
|
||||
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
|
||||
let prevout = TzeOutPoint::read(&mut reader)?;
|
||||
|
||||
let extension_id = CompactSize::read(&mut reader)?;
|
||||
let mode = CompactSize::read(&mut reader)?;
|
||||
let payload = Vector::read(&mut reader, |r| r.read_u8())?;
|
||||
|
||||
Ok(TzeIn {
|
||||
prevout,
|
||||
witness: tze::Witness {
|
||||
extension_id: u32::try_from(extension_id).map_err(to_io_error)?,
|
||||
mode: u32::try_from(mode).map_err(to_io_error)?,
|
||||
payload,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Write without witness data (for signature hashing)
|
||||
///
|
||||
/// This is also used as the prefix for the encoded form used
|
||||
/// within a serialized transaction.
|
||||
pub fn write_without_witness<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
self.prevout.write(&mut writer)?;
|
||||
|
||||
CompactSize::write(
|
||||
&mut writer,
|
||||
usize::try_from(self.witness.extension_id).map_err(to_io_error)?,
|
||||
)?;
|
||||
|
||||
CompactSize::write(
|
||||
&mut writer,
|
||||
usize::try_from(self.witness.mode).map_err(to_io_error)?,
|
||||
)
|
||||
}
|
||||
|
||||
/// Write prevout, extension, and mode followed by witness data.
|
||||
///
|
||||
/// This calls [`write_without_witness`] to serialize witness metadata,
|
||||
/// then appends the witness bytes themselves. This is the encoded
|
||||
/// form that is used in a serialized transaction.
|
||||
///
|
||||
/// [`write_without_witness`]: TzeIn::write_without_witness
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
self.write_without_witness(&mut writer)?;
|
||||
Vector::write(&mut writer, &self.witness.payload, |w, b| w.write_u8(*b))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TzeOut {
|
||||
pub value: Amount,
|
||||
pub precondition: tze::Precondition,
|
||||
}
|
||||
|
||||
impl TzeOut {
|
||||
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
|
||||
let value = {
|
||||
let mut tmp = [0; 8];
|
||||
reader.read_exact(&mut tmp)?;
|
||||
Amount::from_nonnegative_i64_le_bytes(tmp)
|
||||
}
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "value out of range"))?;
|
||||
|
||||
let extension_id = CompactSize::read(&mut reader)?;
|
||||
let mode = CompactSize::read(&mut reader)?;
|
||||
let payload = Vector::read(&mut reader, |r| r.read_u8())?;
|
||||
|
||||
Ok(TzeOut {
|
||||
value,
|
||||
precondition: tze::Precondition {
|
||||
extension_id: u32::try_from(extension_id).map_err(to_io_error)?,
|
||||
mode: u32::try_from(mode).map_err(to_io_error)?,
|
||||
payload,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(&self.value.to_i64_le_bytes())?;
|
||||
|
||||
CompactSize::write(
|
||||
&mut writer,
|
||||
usize::try_from(self.precondition.extension_id).map_err(to_io_error)?,
|
||||
)?;
|
||||
CompactSize::write(
|
||||
&mut writer,
|
||||
usize::try_from(self.precondition.mode).map_err(to_io_error)?,
|
||||
)?;
|
||||
Vector::write(&mut writer, &self.precondition.payload, |w, b| {
|
||||
w.write_u8(*b)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -508,7 +508,7 @@ pub mod testing {
|
|||
};
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
use super::components::{TzeIn, TzeOut};
|
||||
use super::components::{TzeIn, TzeOut, TzeOutPoint};
|
||||
|
||||
pub const VALID_OPCODES: [u8; 8] = [
|
||||
0x00, // OP_FALSE,
|
||||
|
@ -551,6 +551,13 @@ pub mod testing {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
prop_compose! {
|
||||
pub fn arb_tzeoutpoint()(hash in prop::array::uniform32(1u8..), n in 1..100u32) -> TzeOutPoint {
|
||||
TzeOutPoint::new(hash, n)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
prop_compose! {
|
||||
pub fn arb_witness()(extension_id in 0..100u32, mode in 0..100u32, payload in vec(any::<u8>(), 32..256)) -> tze::Witness {
|
||||
|
@ -560,7 +567,7 @@ pub mod testing {
|
|||
|
||||
#[cfg(feature = "zfuture")]
|
||||
prop_compose! {
|
||||
pub fn arb_tzein()(prevout in arb_outpoint(), witness in arb_witness()) -> TzeIn {
|
||||
pub fn arb_tzein()(prevout in arb_tzeoutpoint(), witness in arb_witness()) -> TzeIn {
|
||||
TzeIn { prevout, witness }
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue