Skip to main content

zync_core/
lib.rs

1//! ZYNC Core - Zero-knowledge sYNChronization for Zcash
2//!
3//! Cross-platform wallet core using Crux architecture.
4//! This crate contains all business logic shared across platforms:
5//! - Desktop (egui)
6//! - Android (Jetpack Compose)
7//! - iOS (SwiftUI)
8//! - Web (WASM)
9//!
10//! Key features:
11//! - WASM-compatible parallel note scanning
12//! - Ligerito-powered header chain verification
13//! - Epoch-based proof composition
14
15#![allow(dead_code)]
16#![allow(unused_imports)]
17#![allow(unused_variables)]
18
19pub mod state;
20pub mod error;
21pub mod app;
22pub mod verifier;
23pub mod trustless;
24pub mod scanner;
25
26#[cfg(feature = "client")]
27pub mod client;
28
29// Re-export Crux app
30pub use app::{ZafuCore, Event, Model, ViewModel, Effect, Contact, ChatMessage};
31
32pub use error::{ZyncError, Result};
33pub use state::{WalletState, WalletStateCommitment};
34pub use scanner::{Scanner, BatchScanner, ScanAction, DecryptedNote};
35
36// re-export orchard key types for downstream consumers
37pub use orchard::keys::{FullViewingKey as OrchardFvk, IncomingViewingKey, Scope, SpendingKey};
38
39#[cfg(feature = "client")]
40pub use client::{ZidecarClient, LightwalletdClient};
41// pub use trace::{SyncTrace, TraceField};
42// pub use proof::EpochProof;
43
44use ligerito::{ProverConfig, VerifierConfig};
45use ligerito_binary_fields::{BinaryElem32, BinaryElem128};
46use std::marker::PhantomData;
47
48/// blocks per epoch (~21 hours at 75s/block)
49pub const EPOCH_SIZE: u32 = 1024;
50
51/// max orchard actions per block
52pub const MAX_ACTIONS_PER_BLOCK: usize = 512;
53
54/// fields encoded per action in trace polynomial
55pub const FIELDS_PER_ACTION: usize = 8;
56
57/// polynomial size exponent for tip proofs (2^20 config, max ~32K headers)
58pub const TIP_TRACE_LOG_SIZE: usize = 20;
59
60/// polynomial size exponent for gigaproofs (2^26 config)
61pub const GIGAPROOF_TRACE_LOG_SIZE: usize = 26;
62
63/// security parameter (bits)
64pub const SECURITY_BITS: usize = 100;
65
66/// orchard activation height (mainnet)
67pub const ORCHARD_ACTIVATION_HEIGHT: u32 = 1_687_104;
68
69/// orchard activation height (testnet)
70pub const ORCHARD_ACTIVATION_HEIGHT_TESTNET: u32 = 1_842_420;
71
72/// domain separator for wallet state commitment
73pub const DOMAIN_WALLET_STATE: &[u8] = b"ZYNC_wallet_state_v1";
74
75/// domain separator for epoch proof hash
76pub const DOMAIN_EPOCH_PROOF: &[u8] = b"ZYNC_epoch_proof_v1";
77
78/// domain separator for ivk commitment
79pub const DOMAIN_IVK_COMMIT: &[u8] = b"ZYNC_ivk_commit_v1";
80
81/// genesis epoch hash (all zeros)
82pub const GENESIS_EPOCH_HASH: [u8; 32] = [0u8; 32];
83
84/// empty sparse merkle tree root
85pub const EMPTY_SMT_ROOT: [u8; 32] = [0u8; 32]; // todo: compute actual empty root
86
87/// ligerito prover config for tip proofs (2^20, ~0.1s, max ~32K blocks)
88pub fn tip_prover_config() -> ProverConfig<BinaryElem32, BinaryElem128> {
89    ligerito::hardcoded_config_20(
90        PhantomData::<BinaryElem32>,
91        PhantomData::<BinaryElem128>,
92    )
93}
94
95/// ligerito prover config for gigaproofs (2^26, ~10s, multi-epoch)
96pub fn gigaproof_prover_config() -> ProverConfig<BinaryElem32, BinaryElem128> {
97    ligerito::hardcoded_config_26(
98        PhantomData::<BinaryElem32>,
99        PhantomData::<BinaryElem128>,
100    )
101}
102
103/// select the appropriate prover config for a given trace size
104/// returns (config, required_trace_size) - trace must be padded to required_trace_size
105pub fn prover_config_for_size(trace_len: usize) -> (ProverConfig<BinaryElem32, BinaryElem128>, usize) {
106    let log_size = if trace_len == 0 { 12 } else { (trace_len as f64).log2().ceil() as u32 };
107
108    // available configs: 12, 16, 20, 24, 26, 28, 30
109    let (config_log, config) = if log_size <= 12 {
110        (12, ligerito::hardcoded_config_12(PhantomData::<BinaryElem32>, PhantomData::<BinaryElem128>))
111    } else if log_size <= 16 {
112        (16, ligerito::hardcoded_config_16(PhantomData::<BinaryElem32>, PhantomData::<BinaryElem128>))
113    } else if log_size <= 20 {
114        (20, ligerito::hardcoded_config_20(PhantomData::<BinaryElem32>, PhantomData::<BinaryElem128>))
115    } else if log_size <= 24 {
116        (24, ligerito::hardcoded_config_24(PhantomData::<BinaryElem32>, PhantomData::<BinaryElem128>))
117    } else if log_size <= 26 {
118        (26, ligerito::hardcoded_config_26(PhantomData::<BinaryElem32>, PhantomData::<BinaryElem128>))
119    } else if log_size <= 28 {
120        (28, ligerito::hardcoded_config_28(PhantomData::<BinaryElem32>, PhantomData::<BinaryElem128>))
121    } else {
122        (30, ligerito::hardcoded_config_30(PhantomData::<BinaryElem32>, PhantomData::<BinaryElem128>))
123    };
124
125    (config, 1 << config_log)
126}
127
128/// select the appropriate verifier config for a given log size
129pub fn verifier_config_for_log_size(log_size: u32) -> VerifierConfig {
130    if log_size <= 12 {
131        ligerito::hardcoded_config_12_verifier()
132    } else if log_size <= 16 {
133        ligerito::hardcoded_config_16_verifier()
134    } else if log_size <= 20 {
135        ligerito::hardcoded_config_20_verifier()
136    } else if log_size <= 24 {
137        ligerito::hardcoded_config_24_verifier()
138    } else if log_size <= 26 {
139        ligerito::hardcoded_config_26_verifier()
140    } else if log_size <= 28 {
141        ligerito::hardcoded_config_28_verifier()
142    } else {
143        ligerito::hardcoded_config_30_verifier()
144    }
145}
146
147/// ligerito verifier config for tip proofs (2^24)
148pub fn tip_verifier_config() -> VerifierConfig {
149    ligerito::hardcoded_config_24_verifier()
150}
151
152/// ligerito verifier config for gigaproofs (2^28)
153pub fn gigaproof_verifier_config() -> VerifierConfig {
154    ligerito::hardcoded_config_28_verifier()
155}
156
157/// helper: calculate epoch number from block height
158pub fn epoch_for_height(height: u32) -> u32 {
159    height / EPOCH_SIZE
160}
161
162/// helper: get start height of epoch
163pub fn epoch_start(epoch: u32) -> u32 {
164    epoch * EPOCH_SIZE
165}
166
167/// helper: get end height of epoch (inclusive)
168pub fn epoch_end(epoch: u32) -> u32 {
169    epoch_start(epoch + 1) - 1
170}
171
172/// wallet identifier (random 16 bytes)
173#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
174pub struct WalletId([u8; 16]);
175
176impl WalletId {
177    pub fn random() -> Self {
178        let mut bytes = [0u8; 16];
179        rand::RngCore::fill_bytes(&mut rand::thread_rng(), &mut bytes);
180        Self(bytes)
181    }
182
183    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
184        if bytes.len() != 16 {
185            return Err(ZyncError::InvalidData("wallet id must be 16 bytes".into()));
186        }
187        let mut arr = [0u8; 16];
188        arr.copy_from_slice(bytes);
189        Ok(Self(arr))
190    }
191
192    pub fn to_bytes(&self) -> &[u8; 16] {
193        &self.0
194    }
195}
196
197impl std::fmt::Display for WalletId {
198    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199        write!(f, "{}", hex::encode(&self.0[..8])) // short form
200    }
201}
202
203/// helper: hex encoding (inline to avoid dependency)
204mod hex {
205    pub fn encode(bytes: &[u8]) -> String {
206        bytes.iter().map(|b| format!("{:02x}", b)).collect()
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213
214    #[test]
215    fn test_wallet_id_roundtrip() {
216        let id = WalletId::random();
217        let bytes = id.to_bytes();
218        let id2 = WalletId::from_bytes(bytes).unwrap();
219        assert_eq!(id, id2);
220    }
221
222    // TODO: restore when TRACE_DATA_LOG_SIZE is defined
223    // #[test]
224    // fn test_constants_consistency() {
225    //     // verify trace size calculation
226    //     let blocks = 1 << 10; // EPOCH_SIZE rounded up to power of 2
227    //     let actions = 1 << 9; // MAX_ACTIONS_PER_BLOCK
228    //     let fields = 1 << 3; // FIELDS_PER_ACTION = 8
229    //     assert_eq!(blocks * actions * fields, 1 << TRACE_DATA_LOG_SIZE);
230    // }
231}