test(zcash): restore v1 PCZT end-to-end coverage
What changed, and why it matters
This commit only adds and updates test code for the Zcash cryptocurrency support in the Keystone 3 firmware. It restores an end-to-end test that exercises version 1 PCZT (Partially Created Zcash Transaction) handling for older Orchard transaction formats. There are no changes to production code, user-facing behavior, or security-sensitive logic.
No security action required. Treat as routine test-coverage improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff modifies two Rust files under rust/apps/zcash. It adds a new unit test test_v1_pczt_orchard_check_parse_and_sign and refactors test_support helpers to parameterize transaction version (V5 vs V6) and Orchard bundle version (v2 vs v3). The production parsing, checking, and signing functions are merely called by the new test; their implementations are unchanged. No bug fixes, input validation changes, or cryptographic modifications are present.
Changed components
rust/apps/zcash/src/lib.rsrust/apps/zcash/src/pczt/mod.rsInspect captured patch +74 / −8
diff --git a/rust/apps/zcash/src/lib.rs b/rust/apps/zcash/src/lib.rs
index 777cfd9..56010dd 100644
--- a/rust/apps/zcash/src/lib.rs
+++ b/rust/apps/zcash/src/lib.rs
@@ -1843,6 +1843,56 @@ mod tests {
.unwrap();
}
+ #[test]
+ fn test_v1_pczt_orchard_check_parse_and_sign() {
+ let sample = pczt::test_support::sample_legacy_orchard_change_pczt();
+ let v1_pczt = ::pczt::v1::Pczt::try_from(Pczt::parse(&sample.bytes).unwrap())
+ .unwrap()
+ .serialize();
+ assert_eq!(&v1_pczt[..8], b"PCZT\x01\0\0\0");
+
+ let parsed = parse_pczt_cypherpunk(
+ &MainNetwork,
+ &v1_pczt,
+ &sample.ufvk_text,
+ &sample.seed_fingerprint,
+ )
+ .unwrap();
+ assert!(parsed.get_ironwood().is_none());
+ let orchard = parsed
+ .get_orchard()
+ .expect("v1 Orchard bundle should decode");
+ assert_eq!(orchard.get_from().len(), 1);
+ assert!(orchard.get_from()[0].get_is_mine());
+ assert_eq!(orchard.get_from()[0].get_value(), "0.01 ZEC");
+ assert_eq!(orchard.get_to().len(), 1);
+ assert_eq!(orchard.get_to()[0].get_value(), "0.0099 ZEC");
+ assert_eq!(parsed.get_fee_value(), "0.0001 ZEC");
+
+ let normalized = check_pczt_cypherpunk(
+ &MainNetwork,
+ &v1_pczt,
+ &sample.ufvk_text,
+ &sample.seed_fingerprint,
+ 0,
+ )
+ .unwrap();
+ let signed = sign_checked_pczt(
+ &MainNetwork,
+ &normalized,
+ &sample.seed,
+ &sample.seed_fingerprint,
+ 0,
+ )
+ .unwrap();
+ assert!(Pczt::parse(&signed)
+ .unwrap()
+ .orchard()
+ .actions()
+ .iter()
+ .any(|action| action.spend().spend_auth_sig().is_some()));
+ }
+
#[test]
fn test_parse_ignores_and_check_rejects_unsupported_ironwood_spend_zip32_path() {
let sample = pczt::test_support::sample_ironwood_pczt();
diff --git a/rust/apps/zcash/src/pczt/mod.rs b/rust/apps/zcash/src/pczt/mod.rs
index cc4e024..014ee3c 100644
--- a/rust/apps/zcash/src/pczt/mod.rs
+++ b/rust/apps/zcash/src/pczt/mod.rs
@@ -782,14 +782,22 @@ pub(crate) mod test_support {
}
pub(crate) fn sample_orchard_change_pczt() -> SamplePczt {
- sample_orchard_change_pczt_for_account(0)
+ sample_orchard_change_pczt_for_account(0, TxVersion::V6)
+ }
+
+ pub(crate) fn sample_legacy_orchard_change_pczt() -> SamplePczt {
+ sample_orchard_change_pczt_for_account(0, TxVersion::V5)
}
pub(crate) fn sample_orchard_foreign_change_pczt() -> SamplePczt {
- sample_orchard_change_pczt_for_account(1)
+ sample_orchard_change_pczt_for_account(1, TxVersion::V6)
}
- fn sample_orchard_change_pczt_for_account(output_account: u32) -> SamplePczt {
+ fn sample_orchard_change_pczt_for_account(
+ output_account: u32,
+ tx_version: TxVersion,
+ ) -> SamplePczt {
+ let v6 = tx_version == TxVersion::V6;
let params = MainNetwork;
let seed = [7u8; 32];
let ufvk_text = derive_ufvk(¶ms, &seed, "m/32'/133'/0'").unwrap();
@@ -857,8 +865,16 @@ pub(crate) mod test_support {
let mut builder = orchard::builder::Builder::new(
orchard::builder::BundleType::DEFAULT,
- orchard::bundle::BundleVersion::orchard_v3(),
- orchard::bundle::BundleVersion::orchard_v3().default_flags(),
+ if v6 {
+ orchard::bundle::BundleVersion::orchard_v3()
+ } else {
+ orchard::bundle::BundleVersion::orchard_v2()
+ },
+ if v6 {
+ orchard::bundle::BundleVersion::orchard_v3().default_flags()
+ } else {
+ orchard::bundle::BundleVersion::orchard_v2().default_flags()
+ },
anchor,
)
.expect("default flags are representable under the bundle version");
@@ -878,8 +894,8 @@ pub(crate) mod test_support {
let seed_fingerprint = calculate_seed_fingerprint(&seed).unwrap();
let pczt = Creator::build_from_parts(PcztParts {
params,
- version: TxVersion::V6,
- consensus_branch_id: BranchId::Nu6_3,
+ version: tx_version,
+ consensus_branch_id: if v6 { BranchId::Nu6_3 } else { BranchId::Nu6 },
lock_time: 0,
expiry_height: BlockHeight::from_u32(10_000_000),
transparent: None,
@@ -906,7 +922,7 @@ pub(crate) mod test_support {
})
})
.collect::<Vec<_>>();
- assert_eq!(signing_action_accounts.len(), 2);
+ assert_eq!(signing_action_accounts.len(), if v6 { 2 } else { 1 });
for (action_index, account) in signing_action_accounts {
let derivation = orchard::pczt::Zip32Derivation::parse(
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.