Skip to content

Commit

Permalink
pczt: Encode as header-prefixed Postcard
Browse files Browse the repository at this point in the history
  • Loading branch information
str4d committed Dec 4, 2024
1 parent 5fbd79f commit 79baf8e
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 22 deletions.
75 changes: 75 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions pczt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ zcash_protocol = { workspace = true, optional = true }
blake2b_simd = { workspace = true, optional = true }
rand_core = { workspace = true, optional = true }

# Encoding
postcard = { version = "1", features = ["alloc"] }
serde.workspace = true
serde_with = "3"

# Payment protocols
# - Transparent
secp256k1 = { workspace = true, optional = true }
Expand Down
6 changes: 4 additions & 2 deletions pczt/src/common.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};

use crate::roles::combiner::merge_map;

/// Global fields that are relevant to the transaction as a whole.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct Global {
//
// Transaction effecting data.
Expand Down Expand Up @@ -128,7 +130,7 @@ impl Global {
}
}

#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub(crate) struct Zip32Derivation {
/// The [ZIP 32 seed fingerprint](https://zips.z.cash/zip-0032#seed-fingerprints).
pub(crate) seed_fingerprint: [u8; 32],
Expand Down
47 changes: 46 additions & 1 deletion pczt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,25 @@
//! - Transaction Extractor (anyone can execute)
//! - Creates bindingSig and extracts the final transaction.

use serde::{Deserialize, Serialize};

pub mod roles;

mod common;
mod orchard;
mod sapling;
mod transparent;

const MAGIC_BYTES: &[u8] = b"PCZT";
const PCZT_VERSION_1: u32 = 1;

#[cfg(feature = "zcp-builder")]
const SAPLING_TX_VERSION: u32 = 4;
const V5_TX_VERSION: u32 = 5;
const V5_VERSION_GROUP_ID: u32 = 0x26A7270A;

/// A partially-created Zcash transaction.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Pczt {
/// Global fields that are relevant to the transaction as a whole.
global: common::Global,
Expand All @@ -82,3 +87,43 @@ pub struct Pczt {
sapling: sapling::Bundle,
orchard: orchard::Bundle,
}

impl Pczt {
/// Parses a PCZT from its encoding.
pub fn parse(bytes: &[u8]) -> Result<Self, ParseError> {
if bytes.len() < 8 {
return Err(ParseError::TooShort);

Check warning on line 95 in pczt/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

pczt/src/lib.rs#L93-L95

Added lines #L93 - L95 were not covered by tests
}
if &bytes[..4] != MAGIC_BYTES {
return Err(ParseError::NotPczt);

Check warning on line 98 in pczt/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

pczt/src/lib.rs#L97-L98

Added lines #L97 - L98 were not covered by tests
}
let version = u32::from_le_bytes(bytes[4..8].try_into().unwrap());
if version != PCZT_VERSION_1 {
return Err(ParseError::UnknownVersion(version));

Check warning on line 102 in pczt/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

pczt/src/lib.rs#L100-L102

Added lines #L100 - L102 were not covered by tests
}

// This is a v1 PCZT.
postcard::from_bytes(&bytes[8..]).map_err(ParseError::Postcard)

Check warning on line 106 in pczt/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

pczt/src/lib.rs#L106

Added line #L106 was not covered by tests
}

/// Serializes this PCZT.
pub fn serialize(&self) -> Vec<u8> {
let mut bytes = vec![];
bytes.extend_from_slice(MAGIC_BYTES);
bytes.extend_from_slice(&PCZT_VERSION_1.to_le_bytes());
postcard::to_extend(self, bytes).expect("can serialize into memory")

Check warning on line 114 in pczt/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

pczt/src/lib.rs#L110-L114

Added lines #L110 - L114 were not covered by tests
}
}

/// Errors that can occur while parsing a PCZT.
#[derive(Debug)]
pub enum ParseError {
/// The bytes do not contain a PCZT.
NotPczt,
/// The PCZT encoding was invalid.
Postcard(postcard::Error),
/// The bytes are too short to contain a PCZT.
TooShort,
/// The PCZT has an unknown version.
UnknownVersion(u32),
}
16 changes: 12 additions & 4 deletions pczt/src/orchard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ use std::collections::BTreeMap;

#[cfg(feature = "orchard")]
use ff::PrimeField;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;

use crate::{
common::Zip32Derivation,
roles::combiner::{merge_map, merge_optional},
};

/// PCZT fields that are specific to producing the transaction's Orchard bundle (if any).
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct Bundle {
/// The Orchard actions in this bundle.
///
Expand Down Expand Up @@ -52,7 +54,7 @@ pub(crate) struct Bundle {
pub(crate) bsk: Option<[u8; 32]>,
}

#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct Action {
//
// Action effecting data.
Expand All @@ -77,7 +79,8 @@ pub(crate) struct Action {
}

/// Information about a Sapling spend within a transaction.
#[derive(Clone, Debug)]
#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct Spend {
//
// Spend-specific Action effecting data.
Expand All @@ -91,12 +94,14 @@ pub(crate) struct Spend {
/// The spend authorization signature.
///
/// This is set by the Signer.
#[serde_as(as = "Option<[_; 64]>")]
pub(crate) spend_auth_sig: Option<[u8; 64]>,

/// The address that received the note being spent.
///
/// - This is set by the Constructor (or Updater?).
/// - This is required by the Prover.
#[serde_as(as = "Option<[_; 43]>")]
pub(crate) recipient: Option<[u8; 43]>,

/// The value of the input being spent.
Expand Down Expand Up @@ -128,6 +133,7 @@ pub(crate) struct Spend {
///
/// - This is set by the Updater.
/// - This is required by the Prover.
#[serde_as(as = "Option<[_; 96]>")]
pub(crate) fvk: Option<[u8; 96]>,

/// A witness from the note to the bundle's anchor.
Expand Down Expand Up @@ -160,7 +166,8 @@ pub(crate) struct Spend {
}

/// Information about an Orchard output within a transaction.
#[derive(Clone, Debug)]
#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct Output {
//
// Output-specific Action effecting data.
Expand All @@ -186,6 +193,7 @@ pub(crate) struct Output {
///
/// - This is set by the Constructor.
/// - This is required by the Prover.
#[serde_as(as = "Option<[_; 43]>")]
pub(crate) recipient: Option<[u8; 43]>,

/// The value of the output.
Expand Down
16 changes: 13 additions & 3 deletions pczt/src/sapling.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};
use serde_with::serde_as;

use crate::{
common::Zip32Derivation,
roles::combiner::{merge_map, merge_optional},
Expand All @@ -8,7 +11,7 @@ use crate::{
const GROTH_PROOF_SIZE: usize = 48 + 96 + 48;

/// PCZT fields that are specific to producing the transaction's Sapling bundle (if any).
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct Bundle {
pub(crate) spends: Vec<Spend>,
pub(crate) outputs: Vec<Output>,
Expand All @@ -33,7 +36,8 @@ pub(crate) struct Bundle {
}

/// Information about a Sapling spend within a transaction.
#[derive(Clone, Debug)]
#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct Spend {
//
// SpendDescription effecting data.
Expand All @@ -48,17 +52,20 @@ pub(crate) struct Spend {
/// The Spend proof.
///
/// This is set by the Prover.
#[serde_as(as = "Option<[_; GROTH_PROOF_SIZE]>")]
pub(crate) zkproof: Option<[u8; GROTH_PROOF_SIZE]>,

/// The spend authorization signature.
///
/// This is set by the Signer.
#[serde_as(as = "Option<[_; 64]>")]
pub(crate) spend_auth_sig: Option<[u8; 64]>,

/// The address that received the note being spent.
///
/// - This is set by the Constructor (or Updater?).
/// - This is required by the Prover.
#[serde_as(as = "Option<[_; 43]>")]
pub(crate) recipient: Option<[u8; 43]>,

/// The value of the input being spent.
Expand Down Expand Up @@ -136,7 +143,8 @@ pub(crate) struct Spend {
}

/// Information about a Sapling output within a transaction.
#[derive(Clone, Debug)]
#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct Output {
//
// OutputDescription effecting data.
Expand All @@ -162,12 +170,14 @@ pub(crate) struct Output {
/// The Output proof.
///
/// This is set by the Prover.
#[serde_as(as = "Option<[_; GROTH_PROOF_SIZE]>")]
pub(crate) zkproof: Option<[u8; GROTH_PROOF_SIZE]>,

/// The address that will receive the output.
///
/// - This is set by the Constructor.
/// - This is required by the Prover.
#[serde_as(as = "Option<[_; 43]>")]
pub(crate) recipient: Option<[u8; 43]>,

/// The value of the output.
Expand Down
Loading

0 comments on commit 79baf8e

Please sign in to comment.