diff --git a/pepper-sync/CHANGELOG.md b/pepper-sync/CHANGELOG.md index 39735bdf6c..29242a7010 100644 --- a/pepper-sync/CHANGELOG.md +++ b/pepper-sync/CHANGELOG.md @@ -20,6 +20,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- `config::SyncConfig`: + - added `extended_zip212_grace_period` field. + - updated serialization to version 2 include `extended_zip212_grace_period` field + ### Removed ## [0.3.0] diff --git a/pepper-sync/src/config.rs b/pepper-sync/src/config.rs index 5e0e6a5b25..391057e8cc 100644 --- a/pepper-sync/src/config.rs +++ b/pepper-sync/src/config.rs @@ -86,12 +86,16 @@ pub struct SyncConfig { pub transparent_address_discovery: TransparentAddressDiscovery, /// Performance level pub performance_level: PerformanceLevel, + /// Optionally extend the grace period to enable recovery of zecwalletlite funds. + /// + /// + pub extended_zip212_grace_period: bool, } #[cfg(feature = "wallet_essentials")] impl SyncConfig { fn serialized_version() -> u8 { - 1 + 2 } /// Deserialize into `reader` @@ -101,10 +105,16 @@ impl SyncConfig { let gap_limit = reader.read_u8()?; let scopes = reader.read_u8()?; let performance_level = if version >= 1 { - PerformanceLevel::read(reader)? + PerformanceLevel::read(&mut reader)? } else { PerformanceLevel::High }; + let extended_zip212_grace_period = if version >= 2 { + reader.read_u8()? != 0 + } else { + false + }; + Ok(Self { transparent_address_discovery: TransparentAddressDiscovery { gap_limit, @@ -115,6 +125,7 @@ impl SyncConfig { }, }, performance_level, + extended_zip212_grace_period, }) } @@ -133,7 +144,8 @@ impl SyncConfig { scopes |= 0b100; } writer.write_u8(scopes)?; - self.performance_level.write(writer)?; + self.performance_level.write(&mut writer)?; + writer.write_u8(self.extended_zip212_grace_period as u8)?; Ok(()) } diff --git a/pepper-sync/src/scan.rs b/pepper-sync/src/scan.rs index 09606f4aa5..de0870a789 100644 --- a/pepper-sync/src/scan.rs +++ b/pepper-sync/src/scan.rs @@ -117,6 +117,7 @@ pub(crate) async fn scan

( ufvks: &HashMap, scan_task: ScanTask, max_batch_outputs: usize, + extended_zip212_grace_period: bool, ) -> Result where P: consensus::Parameters + Sync + Send + 'static, @@ -182,6 +183,7 @@ where &ufvks_clone, initial_scan_data, max_batch_outputs / 8, + extended_zip212_grace_period, ) }) .await diff --git a/pepper-sync/src/scan/compact_blocks.rs b/pepper-sync/src/scan/compact_blocks.rs index 2d5a2bc409..40a390935b 100644 --- a/pepper-sync/src/scan/compact_blocks.rs +++ b/pepper-sync/src/scan/compact_blocks.rs @@ -38,6 +38,7 @@ pub(super) fn scan_compact_blocks

( ufvks: &HashMap, initial_scan_data: InitialScanData, trial_decrypt_task_size: usize, + extended_zip212_grace_period: bool, ) -> Result where P: consensus::Parameters + Sync + Send + 'static, @@ -54,6 +55,7 @@ where &scanning_keys, &compact_blocks, trial_decrypt_task_size, + extended_zip212_grace_period, )?; let mut wallet_blocks: BTreeMap = BTreeMap::new(); @@ -181,6 +183,7 @@ fn trial_decrypt

( scanning_keys: &ScanningKeys, compact_blocks: &[CompactBlock], trial_decrypt_task_size: usize, + extended_zip212_grace_period: bool, ) -> Result, ScanError> where P: consensus::Parameters + Send + 'static, @@ -188,7 +191,11 @@ where let mut runners = BatchRunners::<(), ()>::for_keys(trial_decrypt_task_size, scanning_keys); for block in compact_blocks { runners - .add_block(consensus_parameters, block.clone()) + .add_block( + consensus_parameters, + block.clone(), + extended_zip212_grace_period, + ) .map_err(ScanError::ZcbScanError)?; } runners.flush(); diff --git a/pepper-sync/src/scan/compact_blocks/runners.rs b/pepper-sync/src/scan/compact_blocks/runners.rs index 7608d9e605..c4a77964ec 100644 --- a/pepper-sync/src/scan/compact_blocks/runners.rs +++ b/pepper-sync/src/scan/compact_blocks/runners.rs @@ -8,12 +8,14 @@ use std::sync::atomic::AtomicUsize; use crossbeam_channel as channel; use orchard::note_encryption::{CompactAction, OrchardDomain}; +use sapling_crypto::note_encryption::Zip212Enforcement; use sapling_crypto::note_encryption::{CompactOutputDescription, SaplingDomain}; use zcash_client_backend::proto::compact_formats::CompactBlock; use zcash_note_encryption::{BatchDomain, COMPACT_NOTE_SIZE, Domain, ShieldedOutput, batch}; -use zcash_primitives::{ - block::BlockHash, transaction::TxId, transaction::components::sapling::zip212_enforcement, -}; +use zcash_primitives::{block::BlockHash, transaction::TxId}; +use zcash_protocol::consensus::BlockHeight; +use zcash_protocol::consensus::NetworkUpgrade; +use zcash_protocol::consensus::Parameters; use zcash_protocol::{ShieldedProtocol, consensus}; use memuse::DynamicUsage; @@ -23,6 +25,42 @@ use crate::keys::ScanningKeyOps as _; use crate::keys::ScanningKeys; use crate::wallet::OutputId; +/// The "grace period" defined in [ZIP 212]. +/// +/// [ZIP 212]: +const ZIP212_GRACE_PERIOD: u32 = 32256; + +/// An extended grace period used by zecwalletlite and resulting in missing funds for zip212 conformant wallets. +/// +const ZIP212_EXTENDED_GRACE_PERIOD: u32 = 32256 + 161280; + +/// Returns the enforcement policy for ZIP 212 at the given height. +/// If `extended` is true, the grace window will be extended to enable recovery of zecwalletlite funds. +/// https://github.com/zcash/librustzcash/issues/323 +fn zip212_enforcement( + params: &impl Parameters, + height: BlockHeight, + extended: bool, +) -> Zip212Enforcement { + if params.is_nu_active(NetworkUpgrade::Canopy, height) { + let grace_period = if extended { + ZIP212_EXTENDED_GRACE_PERIOD + } else { + ZIP212_GRACE_PERIOD + }; + let grace_period_end_height = + params.activation_height(NetworkUpgrade::Canopy).unwrap() + grace_period; + + if height < grace_period_end_height { + Zip212Enforcement::GracePeriod + } else { + Zip212Enforcement::On + } + } else { + Zip212Enforcement::Off + } +} + type TaggedSaplingBatch = Batch< SaplingDomain, sapling_crypto::note_encryption::CompactOutputDescription, @@ -85,13 +123,15 @@ where &mut self, params: &P, block: CompactBlock, + extended_zip212_grace_period: bool, ) -> Result<(), zcash_client_backend::scanning::ScanError> where P: consensus::Parameters + Send + 'static, { let block_hash = block.hash(); let block_height = block.height(); - let zip212_enforcement = zip212_enforcement(params, block_height); + let zip212_enforcement = + zip212_enforcement(params, block_height, extended_zip212_grace_period); for tx in block.vtx { let txid = tx.txid(); diff --git a/pepper-sync/src/scan/task.rs b/pepper-sync/src/scan/task.rs index 7292425b91..7ed2f06399 100644 --- a/pepper-sync/src/scan/task.rs +++ b/pepper-sync/src/scan/task.rs @@ -66,6 +66,7 @@ pub(crate) struct Scanner

{ fetch_request_sender: mpsc::UnboundedSender, consensus_parameters: P, ufvks: HashMap, + extended_zip212_grace_period: bool, } impl

Scanner

@@ -77,6 +78,7 @@ where scan_results_sender: mpsc::UnboundedSender<(ScanRange, Result)>, fetch_request_sender: mpsc::UnboundedSender, ufvks: HashMap, + extended_zip212_grace_period: bool, ) -> Self { let workers: Vec> = Vec::with_capacity(MAX_WORKER_POOLSIZE); @@ -89,6 +91,7 @@ where fetch_request_sender, consensus_parameters, ufvks, + extended_zip212_grace_period, } } @@ -152,7 +155,7 @@ where self.fetch_request_sender.clone(), self.ufvks.clone(), ); - worker.run(max_batch_outputs); + worker.run(max_batch_outputs, self.extended_zip212_grace_period); self.workers.push(worker); self.unique_id += 1; } @@ -649,7 +652,7 @@ where /// Runs the worker in a new tokio task. /// /// Waits for a scan task and then calls [`crate::scan::scan`] on the given range. - fn run(&mut self, max_batch_outputs: usize) { + fn run(&mut self, max_batch_outputs: usize, extended_zip212_grace_period: bool) { let (scan_task_sender, mut scan_task_receiver) = mpsc::channel::(1); let is_scanning = self.is_scanning.clone(); @@ -668,6 +671,7 @@ where &ufvks, scan_task, max_batch_outputs, + extended_zip212_grace_period, ); let scan_results = match tokio::time::timeout(SCAN_TASK_TIMEOUT, scan_fut).await { diff --git a/pepper-sync/src/sync.rs b/pepper-sync/src/sync.rs index 5ea61ae8b0..3d474a177c 100644 --- a/pepper-sync/src/sync.rs +++ b/pepper-sync/src/sync.rs @@ -425,6 +425,7 @@ where scan_results_sender, fetch_request_sender.clone(), ufvks.clone(), + config.extended_zip212_grace_period, ); scanner.launch(config.performance_level); diff --git a/zingo-cli/src/lib.rs b/zingo-cli/src/lib.rs index cfd277daee..a24c3ecef7 100644 --- a/zingo-cli/src/lib.rs +++ b/zingo-cli/src/lib.rs @@ -503,6 +503,7 @@ fn build_zingo_config(filled_template: &ConfigTemplate) -> std::io::Result WalletSettings { sync_config: SyncConfig { transparent_address_discovery: TransparentAddressDiscovery::minimal(), performance_level: PerformanceLevel::High, + extended_zip212_grace_period: false, }, min_confirmations: NonZeroU32::try_from(1).expect("hard-coded non-zero integer"), } diff --git a/zingolib/src/wallet/disk.rs b/zingolib/src/wallet/disk.rs index 7a824616ab..33e07ef670 100644 --- a/zingolib/src/wallet/disk.rs +++ b/zingolib/src/wallet/disk.rs @@ -351,6 +351,7 @@ impl LightWallet { sync_config: SyncConfig { transparent_address_discovery: TransparentAddressDiscovery::minimal(), performance_level: PerformanceLevel::High, + extended_zip212_grace_period: false, }, min_confirmations: NonZeroU32::try_from(3).unwrap(), }, @@ -580,6 +581,7 @@ impl LightWallet { sync_config: SyncConfig { transparent_address_discovery: TransparentAddressDiscovery::minimal(), performance_level: PerformanceLevel::High, + extended_zip212_grace_period: false, }, min_confirmations: NonZeroU32::try_from(3).unwrap(), }