chore: revert Zcash firmware version changes
What changed, and why it matters
This commit is a straightforward revert that removes a recently-added Zcash firmware-version reporting feature. It strips out code that embedded the Keystone firmware version into Zcash PCZT (partially-created transaction) responses and into Zcash wallet-connection QR data. The change also reverts two simulator-only UI/framework tweaks that were apparently part of the same feature branch. There is no direct evidence in the commit of a security vulnerability being fixed; it reads like a product decision to back out a feature.
Treat as a normal feature revert. If the reverted version-stamping feature was introduced because of a security or compatibility requirement, verify with the project maintainers why it was removed and whether any replacement is planned. No immediate security patch action is indicated by the diff alone.
Security signals we found
Removal of compile-time firmware version extraction from src/config/version.h via build.rs
Removal of global.proprietary 'keystone:fw_version' stamp from signed Zcash PCZT responses
Removal of device_version field from Zcash wallet-connection UR generation
Reversion of simulator-only event-handler guard and async execution queue
Evidence from the diff
The diff reverts the ‘Zcash firmware version changes’ feature. It deletes rust/apps/zcash/build.rs and rust/apps/zcash/src/version.rs, removes the version-stamping logic from sign_pczt, removes the device_version parameter from generate_sync_ur and the C FFI wrapper get_connect_zcash_wallet_ur, and stops passing a firmware version string in the GUI connect-wallet flow. It also reverts simulator-specific changes in gui_keyboard.c (an event-code guard) and gui_model.c (a FIFO async-execution queue), returning them to their prior synchronous/direct-call implementations. The commit message simply says ‘chore: revert Zcash firmware version changes’ and does not mention a security issue.
Changed components
rust/apps/zcashrust/apps/wallets/src/zcash.rsrust/rust_c/src/wallet/cypherpunk_wallet/zcash.rssrc/ui/gui_widgets/multi/cypherpunk/gui_connect_wallet_widgets.csrc/ui/gui_components/gui_keyboard.csrc/ui/gui_model/gui_model.cInspect captured patch +26 / −384
diff --git a/rust/apps/wallets/src/zcash.rs b/rust/apps/wallets/src/zcash.rs
index 5663012..4bdcdad 100644
--- a/rust/apps/wallets/src/zcash.rs
+++ b/rust/apps/wallets/src/zcash.rs
@@ -1,4 +1,4 @@
-use alloc::string::{String, ToString};
+use alloc::string::String;
use alloc::vec::Vec;
@@ -19,7 +19,6 @@ impl_public_struct!(UFVKInfo {
pub fn generate_sync_ur(
key_infos: Vec<UFVKInfo>,
seed_fingerprint: [u8; 32],
- device_version: Option<&str>,
) -> URResult<ZcashAccounts> {
let keys = key_infos
.iter()
@@ -31,10 +30,7 @@ pub fn generate_sync_ur(
))
})
.collect::<URResult<Vec<ZcashUnifiedFullViewingKey>>>()?;
- let mut accounts = ZcashAccounts::new(seed_fingerprint.to_vec(), keys);
- if let Some(version) = device_version {
- accounts.set_device_version(version.to_string());
- }
+ let accounts = ZcashAccounts::new(seed_fingerprint.to_vec(), keys);
Ok(accounts)
}
@@ -60,7 +56,7 @@ mod tests {
},
];
- let result = generate_sync_ur(key_infos, seed_fingerprint, Some("1.2.3"));
+ let result = generate_sync_ur(key_infos, seed_fingerprint);
assert!(result.is_ok());
let accounts = result.unwrap();
diff --git a/rust/apps/zcash/build.rs b/rust/apps/zcash/build.rs
deleted file mode 100644
index ff0e0b3..0000000
--- a/rust/apps/zcash/build.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-use std::env;
-use std::fs;
-use std::io::Write;
-use std::path::Path;
-
-fn main() {
- let version_h = Path::new("../../../src/config/version.h");
- println!("cargo:rerun-if-changed={}", version_h.display());
-
- let contents = fs::read_to_string(version_h).unwrap_or_else(|e| {
- panic!(
- "Failed to read {}: {e}. \
- build.rs expects to run from rust/apps/zcash/ with the repo root three levels up.",
- version_h.display()
- )
- });
-
- let mut major: Option<u8> = None;
- let mut minor: Option<u8> = None;
- let mut build: Option<u8> = None;
-
- for line in contents.lines() {
- let line = line.trim();
- if let Some(val) = line.strip_prefix("#define SOFTWARE_VERSION_MAJOR ") {
- if !val.starts_with('(') {
- major = Some(val.trim().parse().expect("SOFTWARE_VERSION_MAJOR is not a valid u8"));
- }
- } else if let Some(val) = line.strip_prefix("#define SOFTWARE_VERSION_MINOR ") {
- minor = Some(val.trim().parse().expect("SOFTWARE_VERSION_MINOR is not a valid u8"));
- } else if let Some(val) = line.strip_prefix("#define SOFTWARE_VERSION_BUILD ") {
- build = Some(val.trim().parse().expect("SOFTWARE_VERSION_BUILD is not a valid u8"));
- }
- }
-
- let major = major.expect("SOFTWARE_VERSION_MAJOR not found in version.h");
- let minor = minor.expect("SOFTWARE_VERSION_MINOR not found in version.h");
- let build = build.expect("SOFTWARE_VERSION_BUILD not found in version.h");
-
- let out_dir = env::var("OUT_DIR").unwrap();
- let out_path = Path::new(&out_dir).join("version_generated.rs");
- let mut f = fs::File::create(&out_path).unwrap();
- writeln!(
- f,
- "/// Auto-generated from src/config/version.h — do not edit.\n\
- pub const KEYSTONE_FW_VERSION: Version = Version({major}, {minor}, {build});"
- )
- .unwrap();
-}
diff --git a/rust/apps/zcash/src/lib.rs b/rust/apps/zcash/src/lib.rs
index f932195..7dacd23 100644
--- a/rust/apps/zcash/src/lib.rs
+++ b/rust/apps/zcash/src/lib.rs
@@ -3,7 +3,6 @@ extern crate alloc;
pub mod errors;
pub mod pczt;
-pub mod version;
use errors::{Result, ZcashError};
@@ -14,6 +13,7 @@ use alloc::{
use pczt::structs::ParsedPczt;
use zcash_vendor::{
pczt::Pczt,
+ transparent::keys::{NonHardenedChildIndex, TransparentKeyScope},
zcash_keys::keys::{UnifiedAddressRequest, UnifiedFullViewingKey},
zcash_protocol::consensus::{self},
zip32,
@@ -212,9 +212,9 @@ pub fn sign_pczt(pczt: &[u8], seed: &[u8]) -> Result<Vec<u8>> {
mod tests {
use alloc::vec::Vec;
- use ::pczt::roles::creator::Creator;
use consensus::MainNetwork;
use keystore::algorithms::zcash::{calculate_seed_fingerprint, derive_ufvk};
+ use ::pczt::roles::creator::Creator;
use rand_core::OsRng;
use serde::{Deserialize, Serialize};
use zcash_primitives::transaction::{
@@ -256,15 +256,9 @@ mod tests {
}
fn malformed_pczt_with_empty_sapling_bundle_and_nonzero_value_sum() -> Vec<u8> {
- let mut bytes = Creator::new(
- BranchId::Nu6.into(),
- 10,
- MainNetwork.coin_type(),
- [0; 32],
- [0; 32],
- )
- .build()
- .serialize();
+ let mut bytes = Creator::new(BranchId::Nu6.into(), 10, MainNetwork.coin_type(), [0; 32], [0; 32])
+ .build()
+ .serialize();
let mut pczt: PcztMirror = postcard::from_bytes(&bytes[8..]).unwrap();
assert!(pczt.sapling.spends.is_empty());
assert!(pczt.sapling.outputs.is_empty());
@@ -336,12 +330,13 @@ mod tests {
let ufvk_text = derive_ufvk(¶ms, &victim_seed, "m/32'/133'/0'").unwrap();
let ufvk = UnifiedFullViewingKey::decode(¶ms, &ufvk_text).unwrap();
let victim_fvk = ufvk.orchard().unwrap().clone();
- let victim_account = zcash_vendor::transparent::keys::AccountPrivKey::from_seed(
- ¶ms,
- &victim_seed,
- zip32::AccountId::ZERO,
- )
- .unwrap();
+ let victim_account =
+ zcash_vendor::transparent::keys::AccountPrivKey::from_seed(
+ ¶ms,
+ &victim_seed,
+ zip32::AccountId::ZERO,
+ )
+ .unwrap();
let (victim_addr, address_index) = victim_account
.to_account_pubkey()
.derive_external_ivk()
@@ -399,7 +394,8 @@ mod tests {
let pczt_bytes = pczt.serialize();
let seed_fingerprint = calculate_seed_fingerprint(&victim_seed).unwrap();
- let result = parse_pczt_cypherpunk(¶ms, &pczt_bytes, &ufvk_text, &seed_fingerprint);
+ let result =
+ parse_pczt_cypherpunk(¶ms, &pczt_bytes, &ufvk_text, &seed_fingerprint);
match result {
Err(ZcashError::InvalidPczt(_)) => {}
Err(ZcashError::InvalidDataError(msg))
diff --git a/rust/apps/zcash/src/pczt/sign.rs b/rust/apps/zcash/src/pczt/sign.rs
index ffa5d34..d8fadf8 100644
--- a/rust/apps/zcash/src/pczt/sign.rs
+++ b/rust/apps/zcash/src/pczt/sign.rs
@@ -1,17 +1,11 @@
use super::*;
-use crate::version::KEYSTONE_FW_VERSION;
-
-/// `global.proprietary` key stamped into every signed PCZT response.
-/// Value is 3 bytes `[major, minor, build]`. Wallets read this to check
-/// whether the device meets their minimum version requirements.
-const PROP_KEY_FW_VERSION: &str = "keystone:fw_version";
use bitcoin::secp256k1;
use blake2b_simd::Hash;
use keystore::algorithms::secp256k1::get_private_key_by_seed;
use rand_core::OsRng;
use zcash_vendor::{
pczt::{
- roles::{low_level_signer, redactor::Redactor, updater::Updater},
+ roles::{low_level_signer, redactor::Redactor},
Pczt,
},
pczt_ext::{self, PcztSigner},
@@ -118,24 +112,11 @@ pub fn sign_pczt(pczt: Pczt, seed: &[u8]) -> crate::Result<Vec<u8>> {
let signer = pczt_ext::sign_orchard(signer, &SeedSigner { seed })
.map_err(|e| ZcashError::SigningError(e.to_string()))?;
- // Stamp the firmware version into `global.proprietary` so the wallet can
- // tell exactly which version of Keystone firmware produced this signature.
- // The Redactor below intentionally does not touch `global`, so this value
- // survives the redaction pass into the returned bytes.
- let stamped_pczt = Updater::new(signer.finish())
- .update_global_with(|mut g| {
- g.set_proprietary(
- PROP_KEY_FW_VERSION.into(),
- KEYSTONE_FW_VERSION.encode().to_vec(),
- );
- })
- .finish();
-
// Now that we've created the signature, remove the other optional fields from the
// PCZT, to reduce its size for the return trip and make the QR code scanning more
// reliable. The wallet that provided the unsigned PCZT can retain it for combining if
// these fields are needed.
- let signed_pczt = Redactor::new(stamped_pczt)
+ let signed_pczt = Redactor::new(signer.finish())
.redact_orchard_with(|mut r| {
r.redact_actions(|mut ar| {
ar.clear_spend_recipient();
@@ -223,104 +204,11 @@ mod tests {
let result = sign_pczt(pczt, &seed);
assert!(result.is_ok());
- let signed_pczt_bytes = result.unwrap();
+ let signed_pczt = result.unwrap();
// Verify result is a valid PCZT
- let parsed = Pczt::parse(&signed_pczt_bytes).expect("signed PCZT must parse");
+ assert!(Pczt::parse(&signed_pczt).is_ok());
// Verify redaction occurred (size should be smaller as witness data is removed)
- // (The updater stamp adds only a few dozen bytes; redaction strips kilobytes.)
- assert!(signed_pczt_bytes.len() < pczt_bytes.len());
-
- // The firmware version stamp must be present in every signed response,
- // even when the request carried no explicit minimum version.
- let stamp = parsed
- .global()
- .proprietary()
- .get(PROP_KEY_FW_VERSION)
- .expect("firmware version stamp must be present");
- assert_eq!(stamp, &KEYSTONE_FW_VERSION.encode().to_vec());
- }
-
- /// Reusable helper: parse the bundled test PCZT and inject a
- /// test proprietary entry at the global level to verify round-trip.
- fn pczt_with_min_version(min_version: &[u8]) -> Pczt {
- // Same real Orchard→transparent PCZT used by `test_sign_pczt_invalid_seed_fingerprint`.
- let pczt_hex = "50435a5401000000058ace9cb502d5a09cc70c0100f083ae0185010000000180ade2041976a91467f7aa14f177a7e0058c66c7242e086488bd3d1088ac000001237431544d4c4a376b324e344e6172716b3546643575556f38324e58534d624b5267436300000000fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493e010000000000000000000000000000000000000000000000000000000000000000024d2eeb083d7c168f64239c3186d53c72e2b1a3a5140f5250f0963689c08cd61c0999baea13f0be05dc6a2554bb2f8f093f4d20911202567a5ab9fd17bce5142b3f79838a71d14757fcff03ba16486a3efb26c9773ec9596821d1e5f32039fe220001d5d3506f152f62c45198446223abf29e06da700990a779fb60a460712fb666a0ff1fab61e2b2b3566b263d0180b6dc05014b2225d5521d6dbb55ae03d22567ce98b242ba5520bc4e2493ec36fb9211c6350194215c2aa089dfa317c61bab4b9747f4e45abca855e45e00710a3dc5caa40a570186f6f9e818f6674c2df92918a55d20f340944de5c67c1c4a9ee347c2c2d6d71d4753d765f2859a3157f7b05cc3bc7089e3f2c9d5abb3fcb1708e74c790985d3dd90cfe2ed03276dfda527c6e8c08d9a1fdeedcb6aef59d9e5bf0ae5d9477ed030001872727f23f40a96896b66d04de905791bae2bc7ee9dc1f4e4ec5ae493dc2fc1001afb475105f1f5b477c52aa3c32ccf131b0c556b80f55ac555460e6b5148bf85303a0808080088581808008808080800800002585b32c42aa5a12b2763953f09aafed13450eda0c416e32d0978260c4171c375413b91e25fa826399623b6716ae8bbb0b4a1099de22478944627af7e5969aa0c404ffab4d35664c1dafd2d2c0cecf4fb3c8b054179f84b2d35d207077b3d256b429acdee34963c573b55ae20fffce73e0e3e575c8fde9d115e7ffab50b3bee60d2436b72c17677e1d7db141fafa72c7f89002908a7a8de3320e5ad3d1ed0bb545235e136904c5c5e4adfa5a100420ceb2196e5e197e919aeaeefa7cb2a1d98e011539af52d618bfb3ba1dfc2d2c01e9bd67523bb6787eb5a0d28e30ad483c6303efd4796795082cc67ea94ba8548a33da1a5ec7c56174bd6b260f548e83a924b7cdd32980ca489b44e981aa1d81cefe2581eebf3a585fb80542aea4a27862f593203b560a412ba4e737c8f678f239f3d1d07c5a82367435f0a0921c46600eb4f6f7387b3cb5984af98b1337f5148ad6388b62dab7cdc48c66ff81685894c2d1d0fe41716b7cb457fb5bd6ff13e321d2f91c15d431f942d7869955dfeadfff61638266ba38d7ba4db7ffe5ee03550d345715cebd9b378181b5769c22e1b20328165da02eeb5d246c70c008ac0c7f7b1bba2cf8270f013eb99cbc5d534270180f34892fdf08d8c16c518d8b7f62d832d676c65fcae34c640ff30d5bd9d65afeab509117a98374b4b9b016228a65bdd803d6c601d2ad6a654c2fe4487d9c7b088d886c36a6afe63d33f8c474f096500acabbb63968e7408c620cc8139331cf7227e9bdbf4b7bae292e15d310e66186b730f28d0515ac5bb71fcc5de09995fe89d005cc2c7afd0fb8f01b315815d38366ebeb6de9ed565b5d1f2ce14b7795b9ad784851f357beacc454be41aaec506f0148461ba5907043ab8618114bbbede979d7f0e0e0af914750df648079e3625e4f309d13ff74d4ada783203bb3652137abd8327cdd06b9332591c9abdcc0cc16f7fec2e0afd849bef8927b3b0ceeca2b90af7611875b78cf525852ee83e10c8f4cb2c80045cbf33c0801a55eeb15c9dca6e53b3dde8a12daf820f1f76624ee48e3128aaa0ef6f6fb32a0303d89e88be288be1b92a301e893790179ec07711e275f48de2f5f8e0ee7b000091c9d96159746d46f353e67463d7052000000000118c5796d39cd2bc56b0a062c20ebd32feb0b57cc231c262d6703520f8de603211edcf51f6084e3288cbdb02957a02cd68fb84973a6a98260fb60f30951dedb2e1240275687c0bd82a2653a2c212bd3c0ea75cd294f5a4d31dcf507c15461402760282899f6b560858c0b6bd95c708f62d1e856480a52401d0d7d6a642fa1c2a10176072c6147735b785ea4ad9276378885704a44c6246f4630ef1df59438562e055bba6c1411a790727ab27421e6c418df8b65cb636d6786ce9e5b632659f5d32401caffe6271e2d77d8634e67a116926d7566b5eb2f2aadba6498d7a1e120f27f52379bb3f8781090ae47e30b0100011a78b2abbab21b29d79141fdff8a389c2eacde5be75c69ae4c4fabc175aec10a0142b202630def2df1f7cd23fcf362c68194829282c57b0c4d5f0ca023b51a571f01bd466676b53cfc27ba4a94bb4ab3ed19d8db336042e09e1e756b560b5ce7fc05d5dc3269236828f541662db5bfd4ab6e07c4dac2682906ee85eca2d12b6522013dd286fc499141cfebfb53175ea4321e08e8a504604bbc2e9d3e59706a1fa439000130febcd5d0c57c6e3780d6fe1f6c07f01a9d5d7a053ac5562f29304418d33a20000000f7fa16a612e422c34d61c44ae692b255c921239547172fcd26519928a3abb10d22548d840b466f1fed5ccb4c442d97b4b59d1a728455ee1598bae8e316f819bac404c9112693c57e0733d550ddc984d82ecc9047721e7e7bc6f283ba00852e49a4d3cda4dad343a366650b1d75b26025eadc5200113ebcc2a4a7db9ac2291083d76e7a8c04831764caf35e4c18bfc58e58699b4a651ca3686a95a6db7133611b5ce80a14225cdac643311869ea0c4a6d760379f285fa9c396c435361044da7e077f236d589a3eb962129988ea6ccde694cb72fa986748fc106981320f478a1c5402fe75a26dee31ec9fad4240aa19932fa8361c43798aa381c63b0c0b17657ccf37792a28456cfe6562e15d9e4aa26ed2660b6c8fc8a92cd352a6025dabcbed5eba82d88b9df3ba73270ff2f9c44fca8b0c1df8ed4cbfa2a4ebe7d0bcc6e5ce73e43b51e054860d7939ca13d77813b372070fd24cdd9c0e2fad7567471c0279bba19a76f0cdbd3107220821dd676c1df6524c15b87c1318eda418d65f8c66d2a77a65f6894199d44611e60c0291c330d1692bd521aef0e316e2b3f8c377b0d6873b3b645196ba74a79c6e0509869ac66276c3e2dfefd54a12365b5945406e7b673321ed36e89a14a194ae8b864e9ac4684655bae7fcd3123a226f282ac6ac82ca88d6a383d8be90f87f4cb85225f697932abfb4c05cda3b6dadb003621fee663f3fcb8f1c96320a3f148bc106ec231961a8f5142dd614317eef16b81492668a8b8795b85d7b0f737fa8d79e9dc3d78840d158a73dc6d1700ce3a8de2a9f93ff1bc8108703b94fd5bd230a19dd0fd821b832d3508b335e07bac28e95c3ab0eb637334bf166fa2a440ea35c0372bb5a745ee86c727a80f0d0d080fef6642ae7aae1407d6a25c3050c498a52ae300105bded1f19829b10df00e7ba301a9aef2c99ad7c5338b0e259ab97ea852630606b8d59709ca067d32698c8761e0f7d5b76ac07d4860b0fe2992010ba88827bb37cf4e3436488580e79101b366d454f29aa2bdf76725130baa08b38af3a71c251521809c84fe3d086943f39f01d760884b6342fac60c010001c54930d4f4f9946dfe91ac3e94cf5b513871c4a5c0c21137959482da796d2d280000000001c4666732084baff2e402ed7d3e457303c73b77dbd4aa5bc943ac7ca96f3779070398a2e304004aed48232c44dbd0b0b5404063ecc4679436f28c6251cbba91e29388fcd98d0e0001dc2be19f4118dbb7500df3a95e304733b247cea7f8c681f6aaafceb8fc1d7d28";
- let pczt_bytes = hex::decode(pczt_hex).unwrap();
- let base = Pczt::parse(&pczt_bytes).unwrap();
- let min_version = min_version.to_vec();
- Updater::new(base)
- .update_global_with(|mut g| {
- g.set_proprietary("test:min_fw_version".to_string(), min_version);
- })
- .finish()
- }
-
- fn test_seed() -> Vec<u8> {
- hex::decode("d561f5aba9db8b100a9a84197322e522f952171a388ad74eaab1ab9db815be3335c3099a0a2bb0fee57e630db5ed7251412b6bd4b905cf518627411fee3f32dd").unwrap()
- }
-
- #[test]
- fn firmware_equal_version_stamps_response() {
- // Demand the exact running version. Must pass and stamp the response.
- let pczt = pczt_with_min_version(&KEYSTONE_FW_VERSION.encode());
- let signed = sign_pczt(pczt, &test_seed()).expect("equal-version PCZT should sign");
- let parsed = Pczt::parse(&signed).expect("signed PCZT must parse");
-
- let stamp = parsed
- .global()
- .proprietary()
- .get(PROP_KEY_FW_VERSION)
- .expect("firmware version stamp must be present");
- assert_eq!(stamp, &KEYSTONE_FW_VERSION.encode().to_vec());
-
- // The min-version request key the wallet set should survive unchanged
- // so the wallet can correlate response to request if it wants to.
- let request_min = parsed
- .global()
- .proprietary()
- .get("test:min_fw_version")
- .expect("request min-version should round-trip");
- assert_eq!(request_min, &KEYSTONE_FW_VERSION.encode().to_vec());
- }
-
- #[test]
- fn firmware_older_min_version_still_stamps_response() {
- // Wallet demands 1.0.0 — older than firmware. Must pass and stamp.
- let pczt = pczt_with_min_version(&[1, 0, 0]);
- let signed = sign_pczt(pczt, &test_seed()).expect("older-min PCZT should sign");
- let parsed = Pczt::parse(&signed).expect("signed PCZT must parse");
-
- let stamp = parsed
- .global()
- .proprietary()
- .get(PROP_KEY_FW_VERSION)
- .expect("firmware version stamp must be present");
- assert_eq!(stamp, &KEYSTONE_FW_VERSION.encode().to_vec());
- }
-
- #[test]
- fn malformed_min_version_round_trips_and_stamps() {
- // The wallet is the authority on min-version; firmware does not
- // validate the shape of wallet-set proprietary keys on the way in.
- // A short/malformed value from the wallet should still round-trip,
- // and the response must still carry the firmware stamp.
- let pczt = pczt_with_min_version(&[1, 2]);
- let signed = sign_pczt(pczt, &test_seed()).expect("malformed min bytes must not block signing");
- let parsed = Pczt::parse(&signed).expect("signed PCZT must parse");
-
- let stamp = parsed
- .global()
- .proprietary()
- .get(PROP_KEY_FW_VERSION)
- .expect("firmware version stamp must be present");
- assert_eq!(stamp, &KEYSTONE_FW_VERSION.encode().to_vec());
-
- let request_min = parsed
- .global()
- .proprietary()
- .get("test:min_fw_version")
- .expect("wallet-set min key must survive round trip");
- assert_eq!(request_min.as_slice(), &[1u8, 2][..]);
+ assert!(signed_pczt.len() < pczt_bytes.len());
}
}
diff --git a/rust/apps/zcash/src/version.rs b/rust/apps/zcash/src/version.rs
deleted file mode 100644
index 59479e1..0000000
--- a/rust/apps/zcash/src/version.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-//! Firmware version stamped into every signed PCZT response via
-//! `global.proprietary["keystone:fw_version"]`.
-//!
-//! Wallets read this stamp to decide whether the device meets their
-//! minimum version requirements. The firmware itself does not enforce
-//! any minimum, it just reports its own version.
-//!
-//! Version encoding is three raw bytes `[major, minor, build]`, matching the
-//! `SOFTWARE_VERSION_MAJOR / MINOR / BUILD` triple in `src/config/version.h`.
-//!
-//! `KEYSTONE_FW_VERSION` is generated at compile time by `build.rs` from
-//! `src/config/version.h` — no manual sync needed.
-
-use crate::errors::ZcashError;
-use alloc::string::ToString;
-
-// Generated by build.rs from src/config/version.h.
-include!(concat!(env!("OUT_DIR"), "/version_generated.rs"));
-
-/// A `[major, minor, build]` firmware version triple.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-pub struct Version(pub u8, pub u8, pub u8);
-
-impl Version {
- /// Raw 3-byte encoding used in `global.proprietary` values.
- pub fn encode(&self) -> [u8; 3] {
- [self.0, self.1, self.2]
- }
-
- /// Parses the 3-byte encoding.
- pub fn parse(bytes: &[u8]) -> Result<Self, ZcashError> {
- if bytes.len() != 3 {
- return Err(ZcashError::InvalidPczt(
- "firmware version must be 3 bytes [major, minor, build]".to_string(),
- ));
- }
- Ok(Version(bytes[0], bytes[1], bytes[2]))
- }
-
- pub fn major(&self) -> u8 {
- self.0
- }
- pub fn minor(&self) -> u8 {
- self.1
- }
- pub fn build(&self) -> u8 {
- self.2
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- extern crate std;
-
- #[test]
- fn encode_round_trip() {
- let v = KEYSTONE_FW_VERSION;
- let encoded = v.encode();
- assert_eq!(encoded, [v.major(), v.minor(), v.build()]);
- assert_eq!(Version::parse(&encoded).unwrap(), v);
- }
-
- #[test]
- fn parse_rejects_wrong_length() {
- assert!(Version::parse(&[]).is_err());
- assert!(Version::parse(&[1, 2]).is_err());
- assert!(Version::parse(&[1, 2, 3, 4]).is_err());
- }
-
- #[test]
- fn ordering_is_lexicographic() {
- assert!(Version(12, 4, 0) < Version(12, 4, 1));
- assert!(Version(12, 4, 0) < Version(12, 5, 0));
- assert!(Version(12, 4, 0) < Version(13, 0, 0));
- assert!(Version(12, 4, 0) == Version(12, 4, 0));
- }
-}
diff --git a/rust/rust_c/src/wallet/cypherpunk_wallet/zcash.rs b/rust/rust_c/src/wallet/cypherpunk_wallet/zcash.rs
index 22d94ce..32752b6 100644
--- a/rust/rust_c/src/wallet/cypherpunk_wallet/zcash.rs
+++ b/rust/rust_c/src/wallet/cypherpunk_wallet/zcash.rs
@@ -18,7 +18,6 @@ pub unsafe extern "C" fn get_connect_zcash_wallet_ur(
seed_fingerprint: PtrBytes,
seed_fingerprint_len: u32,
zcash_keys: Ptr<CSliceFFI<ZcashKey>>,
- device_version: PtrString,
) -> *mut UREncodeResult {
if seed_fingerprint_len != 32 {
return UREncodeResult::from(URError::UrEncodeError(format!(
@@ -42,12 +41,7 @@ pub unsafe extern "C" fn get_connect_zcash_wallet_ur(
)
})
.collect();
- let version = if device_version.is_null() {
- None
- } else {
- Some(recover_c_char(device_version))
- };
- let result = generate_sync_ur(ufvks, seed_fingerprint, version.as_deref());
+ let result = generate_sync_ur(ufvks, seed_fingerprint);
match result.map(|v| v.try_into()) {
Ok(v) => match v {
Ok(data) => UREncodeResult::encode(
diff --git a/src/ui/gui_components/gui_keyboard.c b/src/ui/gui_components/gui_keyboard.c
index 59b0caa..b50875e 100644
--- a/src/ui/gui_components/gui_keyboard.c
+++ b/src/ui/gui_components/gui_keyboard.c
@@ -1200,13 +1200,6 @@ char *GuiGetTrueWord(const lv_obj_t *obj, uint16_t btn_id)
void KbTextAreaHandler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
-#ifdef COMPILE_SIMULATOR
- // Skip events this handler doesn't process. During view teardown the
- // text area may already be freed, and the strlen below would crash.
- if (code != LV_EVENT_VALUE_CHANGED && code != LV_EVENT_READY && code != LV_EVENT_CANCEL) {
- return;
- }
-#endif
lv_obj_t *ta = lv_event_get_target(e);
uint8_t taLen = strlen(lv_textarea_get_text(ta));
KeyBoard_t *keyBoard = lv_event_get_user_data(e);
diff --git a/src/ui/gui_model/gui_model.c b/src/ui/gui_model/gui_model.c
index 9caad95..1b2dc70 100644
--- a/src/ui/gui_model/gui_model.c
+++ b/src/ui/gui_model/gui_model.c
@@ -116,110 +116,15 @@ static PasswordVerifyResult_t g_passwordVerifyResult;
static bool g_stopCalChecksum = false;
#ifdef COMPILE_SIMULATOR
-// On the real device, AsyncExecute posts to a FreeRTOS background task
-// (FIFO). On the simulator we approximate this with a FIFO queue drained
-// by a 1ms lv_timer — model functions run on the next main-loop tick,
-// after the current event chain unwinds. Using lv_async_call directly
-// doesn't work because lv_timer_create inserts at the list head (LIFO).
-// inData is deep-copied because callers often pass stack buffers.
-#include "lvgl.h"
-
-typedef enum {
- ASYNC_KIND_FUNC,
- ASYNC_KIND_FUNC_WITH_RUNNABLE,
-} AsyncKind_t;
-
-typedef struct AsyncQueueNode {
- AsyncKind_t kind;
- union {
- BackgroundAsyncFunc_t func;
- BackgroundAsyncFuncWithRunnable_t funcWithRunnable;
- } u;
- BackgroundAsyncRunnable_t runnable;
- uint32_t dataLen;
- struct AsyncQueueNode *next;
- uint8_t data[];
-} AsyncQueueNode_t;
-
-static AsyncQueueNode_t *g_asyncQueueHead = NULL;
-static AsyncQueueNode_t *g_asyncQueueTail = NULL;
-static lv_timer_t *g_asyncDrainTimer = NULL;
-
-static void AsyncDrainTimerCb(lv_timer_t *timer)
-{
- (void)timer;
- // Snapshot the head so that any new enqueues from inside the callbacks
- // (e.g. a model function that schedules another AsyncExecute) go at the
- // tail and run on the next drain, not inside this one. This keeps each
- // drain iteration bounded and mirrors the real device's "process the
- // current batch, let signals unwind, handle next batch" semantics.
- AsyncQueueNode_t *current = g_asyncQueueHead;
- g_asyncQueueHead = NULL;
- g_asyncQueueTail = NULL;
- while (current != NULL) {
- AsyncQueueNode_t *node = current;
- current = current->next;
- const void *data = node->dataLen > 0 ? node->data : NULL;
- if (node->kind == ASYNC_KIND_FUNC) {
- node->u.func(data, node->dataLen);
- } else {
- node->u.funcWithRunnable(data, node->dataLen, node->runnable);
- }
- free(node);
- }
-}
-
-static void EnsureAsyncDrainTimer(void)
-{
- if (g_asyncDrainTimer == NULL) {
- // Period 1ms: effectively "run every lv_timer_handler iteration".
- g_asyncDrainTimer = lv_timer_create(AsyncDrainTimerCb, 1, NULL);
- }
-}
-
-static void EnqueueAsync(AsyncQueueNode_t *node)
-{
- node->next = NULL;
- if (g_asyncQueueTail != NULL) {
- g_asyncQueueTail->next = node;
- } else {
- g_asyncQueueHead = node;
- }
- g_asyncQueueTail = node;
- EnsureAsyncDrainTimer();
-}
-
int32_t AsyncExecute(BackgroundAsyncFunc_t func, const void *inData, uint32_t inDataLen)
{
- AsyncQueueNode_t *node = malloc(sizeof(*node) + inDataLen);
- if (node == NULL) {
- return ERR_GENERAL_FAIL;
- }
- node->kind = ASYNC_KIND_FUNC;
- node->u.func = func;
- node->runnable = NULL;
- node->dataLen = inDataLen;
- if (inData != NULL && inDataLen > 0) {
- memcpy(node->data, inData, inDataLen);
- }
- EnqueueAsync(node);
+ func(inData, inDataLen);
return SUCCESS_CODE;
}
int32_t AsyncExecuteRunnable(BackgroundAsyncFuncWithRunnable_t func, const void *inData, uint32_t inDataLen, BackgroundAsyncRunnable_t runnable)
{
- AsyncQueueNode_t *node = malloc(sizeof(*node) + inDataLen);
- if (node == NULL) {
- return ERR_GENERAL_FAIL;
- }
- node->kind = ASYNC_KIND_FUNC_WITH_RUNNABLE;
- node->u.funcWithRunnable = func;
- node->runnable = runnable;
- node->dataLen = inDataLen;
- if (inData != NULL && inDataLen > 0) {
- memcpy(node->data, inData, inDataLen);
- }
- EnqueueAsync(node);
+ func(inData, inDataLen, runnable);
return SUCCESS_CODE;
}
#endif
diff --git a/src/ui/gui_widgets/multi/cypherpunk/gui_connect_wallet_widgets.c b/src/ui/gui_widgets/multi/cypherpunk/gui_connect_wallet_widgets.c
index 8d30390..b0fc21e 100644
--- a/src/ui/gui_widgets/multi/cypherpunk/gui_connect_wallet_widgets.c
+++ b/src/ui/gui_widgets/multi/cypherpunk/gui_connect_wallet_widgets.c
@@ -1,6 +1,5 @@
#include "gui_connect_wallet_widgets.h"
#include "account_public_info.h"
-#include "version.h"
#include "gui.h"
#include "gui_button.h"
#include "gui_hintbox.h"
@@ -359,10 +358,7 @@ UREncodeResult *GuiGetZecData(void)
data[0].key_text = ufvk;
data[0].key_name = GetWalletName();
data[0].index = 0;
- char firmwareVersion[32];
- snprintf(firmwareVersion, sizeof(firmwareVersion), "%d.%d.%d",
- SOFTWARE_VERSION_MAJOR, SOFTWARE_VERSION_MINOR, SOFTWARE_VERSION_BUILD);
- return get_connect_zcash_wallet_ur(sfp, 32, keys, firmwareVersion);
+ return get_connect_zcash_wallet_ur(sfp, 32, keys);
}
void GuiPrepareArConnectWalletView(void)
Why this scored 19/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.