What changed, and why it matters
This commit reorganizes Zcash support in a hardware-wallet firmware so that advanced privacy features (the 'Orchard' shielded pool) are only included when a special 'cypherpunk' build feature is enabled. The default build keeps only transparent and Sapling support, which shrinks the firmware binary. The changes are mostly compile-time feature flags and duplicated code paths for the smaller default build. There is no direct evidence in the commit that this fixes an exploitable security bug; it is a build-size and feature-gating change.
Treat as a routine firmware-size optimization with minor defensive hardening. Review that the new default multi_coins build still validates transparent and Sapling PCZT fields correctly and that disabling Orchard does not accidentally skip required checks. Verify the lock_time error-handling path is covered by tests. No urgent security response is indicated by the diff alone.
Security signals we found
Feature-gating of privacy-sensitive Orchard shielded-pool code
Removal of .expect() panics in favor of fallible error returns for lock_time resolution
Build-system change that could affect which cryptographic code is compiled into production firmware
No explicit security bug, CVE, or vulnerability description in commit message or diff
Evidence from the diff
The patch introduces Cargo feature flags (multi_coins, cypherpunk) across rust/apps/zcash, rust/keystore, rust/rust_c, and rust/zcash_vendor. orchard and pczt/orchard dependencies become optional and are gated behind cypherpunk. Functions that touch Orchard bundles are annotated with #[cfg(feature = “cypherpunk”)], and alternative implementations without Orchard are provided for the default multi_coins feature. The rust_c crate now disables keystore default features and explicitly requests the matching keystore feature. A minor hardening change replaces an .expect(“didn’t fail earlier”) with a proper error return (InvalidRequiredHeightLocktime) in the cypherpunk signing path, and the lock_time lookup in the transparent-only sign() function also uses ok_or instead of expect. The Cargo.lock removes the document-features/litrs transitive dependencies, consistent with disabling default features on pczt.
Changed components
rust/apps/zcashrust/keystorerust/rust_crust/zcash_vendorZcash PCZT parsing/checking/signing codeOrchard shielded-pool supportInspect captured patch +227 / −45
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 7a8e85b..e102ba8 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -1598,15 +1598,6 @@ dependencies = [
"libloading",
]
-[[package]]
-name = "document-features"
-version = "0.2.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
-dependencies = [
- "litrs",
-]
-
[[package]]
name = "downcast-rs"
version = "1.2.1"
@@ -2557,12 +2548,6 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
-[[package]]
-name = "litrs"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
-
[[package]]
name = "lock_api"
version = "0.4.13"
@@ -3118,7 +3103,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ecd86f6f9acfadafa3aca948083a5cc6b8c5ff66fd2044416c20269c3953acd"
dependencies = [
- "document-features",
"ff",
"getset",
"nonempty",
diff --git a/rust/apps/zcash/Cargo.toml b/rust/apps/zcash/Cargo.toml
index d8b1f1d..4250768 100644
--- a/rust/apps/zcash/Cargo.toml
+++ b/rust/apps/zcash/Cargo.toml
@@ -13,7 +13,7 @@ bitcoin = { workspace = true }
thiserror = { workspace = true }
zcash_vendor = { workspace = true }
hex = { workspace = true }
-bitvec = {version = "1.0.1", default-features = false, features = ["alloc"]}
+bitvec = { version = "1.0.1", default-features = false, features = ["alloc"] }
blake2b_simd = { workspace = true }
rand_core = { workspace = true, features = ["getrandom"] }
zcash_note_encryption = "0.4.1"
@@ -22,4 +22,9 @@ zcash_note_encryption = "0.4.1"
keystore = { path = "../../keystore" }
[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
\ No newline at end of file
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
+
+[features]
+default = ["multi_coins"]
+multi_coins = ["zcash_vendor/multi_coins"]
+cypherpunk = ["zcash_vendor/cypherpunk"]
diff --git a/rust/apps/zcash/src/errors.rs b/rust/apps/zcash/src/errors.rs
index c020b5d..c35071c 100644
--- a/rust/apps/zcash/src/errors.rs
+++ b/rust/apps/zcash/src/errors.rs
@@ -1,7 +1,10 @@
use alloc::string::String;
use thiserror;
use thiserror::Error;
-use zcash_vendor::{orchard, transparent};
+use zcash_vendor::transparent;
+
+#[cfg(feature = "cypherpunk")]
+use zcash_vendor::orchard;
pub type Result<T> = core::result::Result<T, ZcashError>;
@@ -17,6 +20,7 @@ pub enum ZcashError {
InvalidPczt(String),
}
+#[cfg(feature = "cypherpunk")]
impl From<orchard::pczt::ParseError> for ZcashError {
fn from(e: orchard::pczt::ParseError) -> Self {
Self::InvalidPczt(alloc::format!("Invalid Orchard bundle: {e:?}"))
diff --git a/rust/apps/zcash/src/pczt/check.rs b/rust/apps/zcash/src/pczt/check.rs
index 1566816..591b82b 100644
--- a/rust/apps/zcash/src/pczt/check.rs
+++ b/rust/apps/zcash/src/pczt/check.rs
@@ -2,7 +2,9 @@
use super::*;
+#[cfg(feature = "cypherpunk")]
use orchard::{keys::FullViewingKey, value::ValueSum};
+
use zcash_vendor::{
pczt::{self, roles::verifier::Verifier, Pczt},
ripemd::Ripemd160,
@@ -13,6 +15,7 @@ use zcash_vendor::{
zip32,
};
+#[cfg(feature = "cypherpunk")]
pub fn check_pczt<P: consensus::Parameters>(
params: &P,
seed_fingerprint: &[u8; 32],
@@ -41,6 +44,27 @@ pub fn check_pczt<P: consensus::Parameters>(
Ok(())
}
+#[cfg(not(feature = "cypherpunk"))]
+pub fn check_pczt<P: consensus::Parameters>(
+ params: &P,
+ seed_fingerprint: &[u8; 32],
+ account_index: zip32::AccountId,
+ ufvk: &UnifiedFullViewingKey,
+ pczt: &Pczt,
+) -> Result<(), ZcashError> {
+ // checking xpub and orchard keys.
+ let xpub = ufvk.transparent().ok_or(ZcashError::InvalidDataError(
+ "transparent xpub is not present".to_string(),
+ ))?;
+ Verifier::new(pczt.clone())
+ .with_transparent(|bundle| {
+ check_transparent(params, seed_fingerprint, account_index, xpub, bundle)
+ .map_err(pczt::roles::verifier::TransparentError::Custom)
+ })
+ .map_err(|e| ZcashError::InvalidDataError(alloc::format!("{e:?}")))?;
+ Ok(())
+}
+
fn check_transparent<P: consensus::Parameters>(
params: &P,
seed_fingerprint: &[u8; 32],
@@ -242,6 +266,8 @@ fn check_transparent_output<P: consensus::Parameters>(
}
}
+
+#[cfg(feature="cypherpunk")]
// check orchard bundle
fn check_orchard<P: consensus::Parameters>(
params: &P,
@@ -273,6 +299,7 @@ fn check_orchard<P: consensus::Parameters>(
}
}
+#[cfg(feature="cypherpunk")]
// check orchard action
fn check_action<P: consensus::Parameters>(
params: &P,
@@ -291,6 +318,7 @@ fn check_action<P: consensus::Parameters>(
check_action_output(action)
}
+#[cfg(feature="cypherpunk")]
// check spend nullifier
fn check_action_spend<P: consensus::Parameters>(
params: &P,
@@ -332,6 +360,7 @@ fn check_action_spend<P: consensus::Parameters>(
Ok(())
}
+#[cfg(feature="cypherpunk")]
//check output cmx
fn check_action_output(action: &orchard::pczt::Action) -> Result<(), ZcashError> {
action
diff --git a/rust/apps/zcash/src/pczt/mod.rs b/rust/apps/zcash/src/pczt/mod.rs
index fb11efa..a0624c8 100644
--- a/rust/apps/zcash/src/pczt/mod.rs
+++ b/rust/apps/zcash/src/pczt/mod.rs
@@ -1,8 +1,14 @@
use alloc::{string::ToString, vec::Vec};
use keystore::algorithms::secp256k1::get_public_key_by_seed;
-use keystore::algorithms::zcash::{calculate_seed_fingerprint, sign_message_orchard};
-use zcash_vendor::{orchard, zcash_keys::keys::UnifiedFullViewingKey};
+use keystore::algorithms::zcash::calculate_seed_fingerprint;
+use zcash_vendor::zcash_keys::keys::UnifiedFullViewingKey;
+
+#[cfg(feature = "cypherpunk")]
+use zcash_vendor::orchard;
+
+#[cfg(feature = "cypherpunk")]
+use keystore::algorithms::zcash::sign_message_orchard;
use crate::errors::ZcashError;
diff --git a/rust/apps/zcash/src/pczt/parse.rs b/rust/apps/zcash/src/pczt/parse.rs
index c2857fa..6a127e0 100644
--- a/rust/apps/zcash/src/pczt/parse.rs
+++ b/rust/apps/zcash/src/pczt/parse.rs
@@ -4,12 +4,9 @@ use alloc::{
vec,
};
use zcash_note_encryption::{
- try_output_recovery_with_ovk, try_output_recovery_with_pkd_esk, Domain,
+ try_output_recovery_with_ovk, try_output_recovery_with_pkd_esk,
};
use zcash_vendor::{
- orchard::{
- self, keys::OutgoingViewingKey, note::Note, note_encryption::OrchardDomain, Address,
- },
pczt::{self, roles::verifier::Verifier, Pczt},
ripemd::{Digest, Ripemd160},
sha2::Sha256,
@@ -25,6 +22,11 @@ use zcash_vendor::{
},
};
+#[cfg(feature = "cypherpunk")]
+use zcash_vendor::orchard::{
+ self, keys::OutgoingViewingKey, note::Note, note_encryption::OrchardDomain, Address,
+};
+
use crate::errors::ZcashError;
use super::structs::{ParsedFrom, ParsedOrchard, ParsedPczt, ParsedTo, ParsedTransparent};
@@ -48,6 +50,7 @@ fn format_zec_value(value: f64) -> String {
/// - `Ok(None)` if the output cannot be decrypted.
/// - `Err(_)` if `ovk` is `None` and the PCZT is missing fields needed to directly
/// decrypt the output.
+#[cfg(feature="cypherpunk")]
pub fn decode_output_enc_ciphertext(
action: &orchard::pczt::Action,
ovk: Option<&OutgoingViewingKey>,
@@ -114,6 +117,7 @@ pub fn decode_output_enc_ciphertext(
/// 3. Handles Sapling pool interactions (though full Sapling decoding is not supported)
/// 4. Computes transfer values and fees
/// 5. Returns a structured representation of the transaction
+#[cfg(feature = "orchard")]
pub fn parse_pczt<P: consensus::Parameters>(
params: &P,
seed_fingerprint: &[u8; 32],
@@ -208,6 +212,78 @@ pub fn parse_pczt<P: consensus::Parameters>(
has_sapling,
))
}
+#[cfg(not(feature = "orchard"))]
+pub fn parse_pczt<P: consensus::Parameters>(
+ params: &P,
+ seed_fingerprint: &[u8; 32],
+ ufvk: &UnifiedFullViewingKey,
+ pczt: &Pczt,
+) -> Result<ParsedPczt, ZcashError> {
+ let mut parsed_transparent = None;
+
+ Verifier::new(pczt.clone())
+ .with_transparent(|bundle| {
+ parsed_transparent = parse_transparent(params, seed_fingerprint, bundle)
+ .map_err(pczt::roles::verifier::TransparentError::Custom)?;
+ Ok(())
+ })
+ .map_err(|e| ZcashError::InvalidDataError(alloc::format!("{e:?}")))?;
+
+ let mut total_input_value = 0;
+ let mut total_output_value = 0;
+ let mut total_change_value = 0;
+ //total_input_value = total_output_value + fee_value
+ //total_output_value = total_transfer_value + total_change_value
+
+ if let Some(transparent) = &parsed_transparent {
+ total_change_value += transparent
+ .get_to()
+ .iter()
+ .filter(|v| v.get_is_change())
+ .fold(0, |acc, to| acc + to.get_amount());
+ total_input_value += transparent
+ .get_from()
+ .iter()
+ .fold(0, |acc, from| acc + from.get_amount());
+ total_output_value += transparent
+ .get_to()
+ .iter()
+ .fold(0, |acc, to| acc + to.get_amount());
+ }
+
+ //treat all sapling output as output value since we don't support sapling decoding yet
+ //sapling value_sum can be trusted
+
+ let value_balance = (*pczt.sapling().value_sum())
+ .try_into()
+ .ok()
+ .and_then(|v| ZatBalance::from_i64(v).ok())
+ .ok_or(ZcashError::InvalidPczt(
+ "sapling value_sum is invalid".to_string(),
+ ))?;
+ let sapling_value_sum: i64 = value_balance.into();
+ if sapling_value_sum < 0 {
+ //value transfered to sapling pool
+ total_output_value = total_output_value.saturating_add(sapling_value_sum.unsigned_abs())
+ } else {
+ //value transfered from sapling pool
+ //this should not happen with Zashi.
+ total_input_value = total_input_value.saturating_add(sapling_value_sum as u64)
+ };
+
+ let total_transfer_value = format_zec_value((total_output_value - total_change_value) as f64);
+ let fee_value = format_zec_value((total_input_value - total_output_value) as f64);
+
+ let has_sapling = !pczt.sapling().spends().is_empty() || !pczt.sapling().outputs().is_empty();
+
+ Ok(ParsedPczt::new(
+ parsed_transparent,
+ None,
+ total_transfer_value,
+ fee_value,
+ has_sapling,
+ ))
+}
fn parse_transparent<P: consensus::Parameters>(
params: &P,
@@ -335,6 +411,7 @@ fn parse_transparent_output(
}
}
+#[cfg(feature="cypherpunk")]
fn parse_orchard<P: consensus::Parameters>(
params: &P,
seed_fingerprint: &[u8; 32],
@@ -367,6 +444,7 @@ fn parse_orchard<P: consensus::Parameters>(
}
}
+#[cfg(feature="cypherpunk")]
fn parse_orchard_spend(
seed_fingerprint: &[u8; 32],
spend: &orchard::pczt::Spend,
@@ -387,6 +465,7 @@ fn parse_orchard_spend(
Ok(ParsedFrom::new(None, zec_value, value, is_mine))
}
+#[cfg(feature="cypherpunk")]
fn parse_orchard_output<P: consensus::Parameters>(
params: &P,
ufvk: &UnifiedFullViewingKey,
diff --git a/rust/apps/zcash/src/pczt/sign.rs b/rust/apps/zcash/src/pczt/sign.rs
index 79b7576..d862030 100644
--- a/rust/apps/zcash/src/pczt/sign.rs
+++ b/rust/apps/zcash/src/pczt/sign.rs
@@ -75,6 +75,7 @@ impl PcztSigner for SeedSigner<'_> {
Ok(())
}
+ #[cfg(feature = "orchard")]
fn sign_orchard(
&self,
action: &mut orchard::pczt::Action,
diff --git a/rust/keystore/Cargo.toml b/rust/keystore/Cargo.toml
index 6afed1c..1d0a284 100644
--- a/rust/keystore/Cargo.toml
+++ b/rust/keystore/Cargo.toml
@@ -24,9 +24,12 @@ zcash_vendor = { workspace = true }
zeroize = { workspace = true }
[features]
-default = ["std"]
+default = ["std", "multi_coins"]
std = []
rsa = []
+multi_coins = ["zcash_vendor/multi_coins"]
+cypherpunk = ["zcash_vendor/cypherpunk"]
[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
\ No newline at end of file
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
+
diff --git a/rust/keystore/src/algorithms/zcash/mod.rs b/rust/keystore/src/algorithms/zcash/mod.rs
index 1b2a6b2..feb747d 100644
--- a/rust/keystore/src/algorithms/zcash/mod.rs
+++ b/rust/keystore/src/algorithms/zcash/mod.rs
@@ -4,16 +4,19 @@ use alloc::string::{String, ToString};
use bitcoin::bip32::{ChildNumber, DerivationPath};
use rand_core::{CryptoRng, RngCore};
use zcash_vendor::{
- orchard::{
- self,
- keys::{SpendAuthorizingKey, SpendingKey},
- },
zcash_keys::keys::UnifiedSpendingKey,
zcash_protocol::consensus,
zip32::{self, fingerprint::SeedFingerprint},
};
use crate::algorithms::utils::is_all_zero_or_ff;
+
+#[cfg(feature = "cypherpunk")]
+use zcash_vendor::orchard::{
+ self,
+ keys::{SpendAuthorizingKey, SpendingKey},
+};
+
use crate::errors::{KeystoreError, Result};
pub fn derive_ufvk<P: consensus::Parameters>(
@@ -67,6 +70,7 @@ pub fn calculate_seed_fingerprint(seed: &[u8]) -> Result<[u8; 32]> {
Ok(sfp.to_bytes())
}
+#[cfg(feature = "cypherpunk")]
pub fn sign_message_orchard<R: RngCore + CryptoRng>(
action: &mut orchard::pczt::Action,
seed: &[u8],
diff --git a/rust/rust_c/Cargo.toml b/rust/rust_c/Cargo.toml
index f116103..1fd28ae 100644
--- a/rust/rust_c/Cargo.toml
+++ b/rust/rust_c/Cargo.toml
@@ -28,7 +28,7 @@ cipher = { workspace = true }
minicbor = { workspace = true }
#keystone owned
sim_qr_reader = { workspace = true, optional = true }
-keystore = { workspace = true }
+keystore = { workspace = true, default-features = false}
ur-registry = { workspace = true }
ur-parse-lib = { workspace = true }
zcash_vendor = { workspace = true, optional = true }
@@ -82,7 +82,8 @@ sui = ["dep:app_sui"]
ton = ["dep:app_ton"]
tron = ["dep:app_tron"]
xrp = ["dep:app_xrp"]
-zcash = ["dep:app_zcash", "dep:zcash_vendor"]
+zcash = ["dep:app_zcash", "dep:zcash_vendor", "app_zcash/multi_coins", "zcash_vendor/multi_coins"]
+zcash_cypherpunk = ["dep:app_zcash", "dep:zcash_vendor", "app_zcash/cypherpunk", "zcash_vendor/cypherpunk"]
monero = ["dep:app_monero"]
avalanche = ["dep:app_avalanche"]
iota = ["dep:app_iota"]
@@ -110,12 +111,13 @@ multi-coins = [
"xrp",
"avalanche",
"iota",
- "zcash"
+ "zcash",
+ "keystore/multi_coins",
]
btc-only = ["bitcoin"]
-cypherpunk = ["bitcoin", "zcash", "monero"]
+cypherpunk = ["bitcoin", "zcash_cypherpunk", "monero", "keystore/cypherpunk"]
# build variants
# production
diff --git a/rust/zcash_vendor/Cargo.toml b/rust/zcash_vendor/Cargo.toml
index cac935c..dfb3f70 100644
--- a/rust/zcash_vendor/Cargo.toml
+++ b/rust/zcash_vendor/Cargo.toml
@@ -41,22 +41,44 @@ chacha20poly1305 = { version = "0.10.1", default-features = false, features = [
] }
postcard = { version = "1.0.3", features = ["alloc"] }
getset = { version = "0.1.3" }
-orchard = { version = "0.11", default-features = false }
-pczt = { version = "0.2", features = ["orchard", "transparent"] }
+orchard = { version = "0.11", default-features = false, optional = true }
+pczt = { version = "0.2", default-features = false }
serde = { workspace = true }
-serde_with = { version = "3.11.0", features = ["alloc", "macros"], default-features = false }
-transparent = { package = "zcash_transparent", version = "0.2", default-features = false, features = ["transparent-inputs"] }
+serde_with = { version = "3.11.0", features = [
+ "alloc",
+ "macros",
+], default-features = false }
+transparent = { package = "zcash_transparent", version = "0.2", default-features = false, features = [
+ "transparent-inputs",
+] }
zcash_address = { version = "0.7", default-features = false }
zcash_encoding = { version = "0.3", default-features = false }
-zcash_keys = { version = "0.8", default-features = false, features = ["orchard", "transparent-inputs"] }
+zcash_keys = { version = "0.8", default-features = false }
zcash_protocol = { version = "0.5", default-features = false }
zip32 = { version = "0.2", default-features = false }
rust_tools = { workspace = true }
#zcash end
[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(zcash_unstable, values("zfuture"))'] }
+unexpected_cfgs = { level = "warn", check-cfg = [
+ 'cfg(zcash_unstable, values("zfuture"))',
+] }
[dev-dependencies]
-transparent = { package = "zcash_transparent", version = "0.2", default-features = false, features = ["transparent-inputs", "test-dependencies"] }
-incrementalmerkletree-testing = {version = "0.3"}
\ No newline at end of file
+transparent = { package = "zcash_transparent", version = "0.2", default-features = false, features = [
+ "transparent-inputs",
+ "test-dependencies",
+] }
+incrementalmerkletree-testing = { version = "0.3" }
+
+
+[features]
+default = ["multi_coins"]
+multi_coins = ["pczt/transparent", "zcash_keys/transparent-inputs"]
+cypherpunk = [
+ "pczt/transparent",
+ "zcash_keys/transparent-inputs",
+ "pczt/orchard",
+ "zcash_keys/orchard",
+ "dep:orchard",
+]
diff --git a/rust/zcash_vendor/src/lib.rs b/rust/zcash_vendor/src/lib.rs
index ecaf907..a4e8709 100644
--- a/rust/zcash_vendor/src/lib.rs
+++ b/rust/zcash_vendor/src/lib.rs
@@ -4,6 +4,7 @@ pub mod pczt_ext;
extern crate alloc;
pub use bip32;
+#[cfg(feature = "cypherpunk")]
pub use orchard;
pub use pasta_curves;
pub use pczt;
diff --git a/rust/zcash_vendor/src/pczt_ext.rs b/rust/zcash_vendor/src/pczt_ext.rs
index cb74be5..4d9e52f 100644
--- a/rust/zcash_vendor/src/pczt_ext.rs
+++ b/rust/zcash_vendor/src/pczt_ext.rs
@@ -92,6 +92,7 @@ pub type TransparentSignatureDER = Vec<u8>;
pub trait PcztSigner {
type Error;
+ #[cfg(feature = "multi_coins")]
fn sign_transparent<F>(
&self,
index: usize,
@@ -100,6 +101,7 @@ pub trait PcztSigner {
) -> Result<(), Self::Error>
where
F: FnOnce(SignableInput) -> [u8; 32];
+ #[cfg(feature = "cypherpunk")]
fn sign_orchard(
&self,
action: &mut orchard::pczt::Action,
@@ -387,10 +389,49 @@ fn transparent_sig_digest(pczt: &Pczt, input_info: Option<SignableInput>) -> Has
}
}
+#[cfg(feature = "multi_coins")]
+pub fn sign<T>(llsigner: Signer, signer: &T) -> Result<Signer, T::Error>
+where
+ T: PcztSigner,
+ T::Error: From<transparent::pczt::ParseError>,
+{
+ llsigner.sign_transparent_with::<T::Error, _>(|pczt, signable, tx_modifiable| {
+ let lock_time = determine_lock_time(pczt.global(), pczt.transparent().inputs())
+ .ok_or(transparent::pczt::ParseError::InvalidRequiredHeightLocktime)?;
+ signable
+ .inputs_mut()
+ .iter_mut()
+ .enumerate()
+ .try_for_each(|(i, input)| {
+ signer.sign_transparent(i, input, |signable_input| {
+ sheilded_sig_commitment(pczt, lock_time, Some(signable_input))
+ .as_bytes()
+ .try_into()
+ .expect("correct length")
+ })?;
+
+ if input.sighash_type().encode() & SIGHASH_ANYONECANPAY == 0 {
+ *tx_modifiable &= !FLAG_TRANSPARENT_INPUTS_MODIFIABLE;
+ }
+
+ if (input.sighash_type().encode() & !SIGHASH_ANYONECANPAY) != SIGHASH_NONE {
+ *tx_modifiable &= !FLAG_TRANSPARENT_OUTPUTS_MODIFIABLE;
+ }
+
+ if (input.sighash_type().encode() & !SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE {
+ *tx_modifiable |= FLAG_HAS_SIGHASH_SINGLE;
+ }
+
+ *tx_modifiable &= !FLAG_SHIELDED_MODIFIABLE;
+ Ok(())
+ })
+ })
+}
+
+#[cfg(feature = "cypherpunk")]
pub fn sign<T>(llsigner: Signer, signer: &T) -> Result<Signer, T::Error>
where
T: PcztSigner,
- T::Error: From<orchard::pczt::ParseError>,
T::Error: From<transparent::pczt::ParseError>,
{
llsigner
@@ -427,7 +468,7 @@ where
})?
.sign_orchard_with::<T::Error, _>(|pczt, signable, tx_modifiable| {
let lock_time = determine_lock_time(pczt.global(), pczt.transparent().inputs())
- .expect("didn't fail earlier");
+ .ok_or(transparent::pczt::ParseError::InvalidRequiredHeightLocktime)?;
signable.actions_mut().iter_mut().try_for_each(|action| {
match action.spend().value().map(|v| v.inner()) {
//dummy spend maybe
@@ -447,6 +488,7 @@ where
})
}
+#[cfg(feature = "cypherpunk")]
#[cfg(test)]
mod tests {
use pczt::Pczt;
Why this scored 18/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.