Skip to main content

pepper_sync/wallet/
serialization.rs

1//! Serialization and de-serialization of wallet structs in [`crate::wallet`] including utilities.
2
3use std::{
4    collections::{BTreeMap, BTreeSet},
5    io::{Read, Write},
6    ops::Range,
7};
8
9use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
10
11use incrementalmerkletree::{Hashable, Position};
12use shardtree::{
13    LocatedPrunableTree, ShardTree,
14    store::{Checkpoint, ShardStore, TreeState, memory::MemoryShardStore},
15};
16use zcash_client_backend::serialization::shardtree::{read_shard, write_shard};
17use zcash_encoding::{Optional, Vector};
18use zcash_primitives::{
19    block::BlockHash,
20    merkle_tree::HashSer,
21    transaction::{Transaction, TxId},
22};
23use zcash_protocol::{
24    consensus::{self, BlockHeight},
25    memo::Memo,
26    value::Zatoshis,
27};
28use zcash_transparent::address::Script;
29
30use zcash_transparent::keys::NonHardenedChildIndex;
31use zingo_status::confirmation_status::ConfirmationStatus;
32
33use crate::{
34    keys::{
35        KeyId, decode_unified_address,
36        transparent::{TransparentAddressId, TransparentScope},
37    },
38    sync::{MAX_REORG_ALLOWANCE, ScanPriority, ScanRange},
39    wallet::ScanTarget,
40};
41
42use super::{
43    InitialSyncState, KeyIdInterface, NullifierMap, OrchardNote, OutgoingNote,
44    OutgoingNoteInterface, OutgoingOrchardNote, OutgoingSaplingNote, OutputId, OutputInterface,
45    SaplingNote, ShardTrees, SyncState, TransparentCoin, TreeBounds, WalletBlock, WalletNote,
46    WalletTransaction,
47};
48
49fn read_string<R: Read>(mut reader: R) -> std::io::Result<String> {
50    let str_len = reader.read_u64::<LittleEndian>()?;
51    let mut str_bytes = vec![0; str_len as usize];
52    reader.read_exact(&mut str_bytes)?;
53
54    String::from_utf8(str_bytes)
55        .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))
56}
57
58fn write_string<W: Write>(mut writer: W, str: &str) -> std::io::Result<()> {
59    writer.write_u64::<LittleEndian>(str.len() as u64)?;
60    writer.write_all(str.as_bytes())
61}
62
63impl ScanTarget {
64    fn serialized_version() -> u8 {
65        0
66    }
67
68    /// Deserialize into `reader`
69    pub fn read<R: Read>(mut reader: R) -> std::io::Result<Self> {
70        let _version = reader.read_u8()?;
71        let block_height = BlockHeight::from_u32(reader.read_u32::<LittleEndian>()?);
72        let txid = TxId::read(&mut reader)?;
73        let narrow_scan_area = reader.read_u8()? != 0;
74
75        Ok(Self {
76            block_height,
77            txid,
78            narrow_scan_area,
79        })
80    }
81
82    /// Serialize into `writer`
83    pub fn write<W: Write>(&self, writer: &mut W) -> std::io::Result<()> {
84        writer.write_u8(Self::serialized_version())?;
85        writer.write_u32::<LittleEndian>(self.block_height.into())?;
86        self.txid.write(&mut *writer)?;
87        writer.write_u8(u8::from(self.narrow_scan_area))
88    }
89}
90
91impl SyncState {
92    fn serialized_version() -> u8 {
93        3
94    }
95
96    /// Deserialize into `reader`
97    pub fn read<R: Read>(mut reader: R) -> std::io::Result<Self> {
98        let version = reader.read_u8()?;
99        let scan_ranges = Vector::read(&mut reader, |r| {
100            let start = BlockHeight::from_u32(r.read_u32::<LittleEndian>()?);
101            let end = BlockHeight::from_u32(r.read_u32::<LittleEndian>()?);
102            let priority = match version {
103                3.. => match r.read_u8()? {
104                    0 => Ok(ScanPriority::RefetchingNullifiers),
105                    1 => Ok(ScanPriority::Scanning),
106                    2 => Ok(ScanPriority::Scanned),
107                    3 => Ok(ScanPriority::ScannedWithoutMapping),
108                    4 => Ok(ScanPriority::Historic),
109                    5 => Ok(ScanPriority::OpenAdjacent),
110                    6 => Ok(ScanPriority::FoundNote),
111                    7 => Ok(ScanPriority::ChainTip),
112                    8 => Ok(ScanPriority::Verify),
113                    _ => Err(std::io::Error::new(
114                        std::io::ErrorKind::InvalidData,
115                        "invalid scan priority",
116                    )),
117                }?,
118                2 => match r.read_u8()? {
119                    0 => Ok(ScanPriority::Scanning),
120                    1 => Ok(ScanPriority::Scanned),
121                    2 => Ok(ScanPriority::ScannedWithoutMapping),
122                    3 => Ok(ScanPriority::Historic),
123                    4 => Ok(ScanPriority::OpenAdjacent),
124                    5 => Ok(ScanPriority::FoundNote),
125                    6 => Ok(ScanPriority::ChainTip),
126                    7 => Ok(ScanPriority::Verify),
127                    _ => Err(std::io::Error::new(
128                        std::io::ErrorKind::InvalidData,
129                        "invalid scan priority",
130                    )),
131                }?,
132                0 | 1 => match r.read_u8()? {
133                    0 => Ok(ScanPriority::Scanning),
134                    1 => Ok(ScanPriority::Scanned),
135                    2 => Ok(ScanPriority::Historic),
136                    3 => Ok(ScanPriority::OpenAdjacent),
137                    4 => Ok(ScanPriority::FoundNote),
138                    5 => Ok(ScanPriority::ChainTip),
139                    6 => Ok(ScanPriority::Verify),
140                    _ => Err(std::io::Error::new(
141                        std::io::ErrorKind::InvalidData,
142                        "invalid scan priority",
143                    )),
144                }?,
145            };
146
147            Ok(ScanRange::from_parts(start..end, priority))
148        })?;
149        let sapling_shard_ranges = Vector::read(&mut reader, |r| {
150            let start = BlockHeight::from_u32(r.read_u32::<LittleEndian>()?);
151            let end = BlockHeight::from_u32(r.read_u32::<LittleEndian>()?);
152
153            Ok(start..end)
154        })?;
155        let orchard_shard_ranges = Vector::read(&mut reader, |r| {
156            let start = BlockHeight::from_u32(r.read_u32::<LittleEndian>()?);
157            let end = BlockHeight::from_u32(r.read_u32::<LittleEndian>()?);
158
159            Ok(start..end)
160        })?;
161        let scan_targets = Vector::read(&mut reader, |r| {
162            Ok(if version >= 1 {
163                ScanTarget::read(r)?
164            } else {
165                let block_height = BlockHeight::from_u32(r.read_u32::<LittleEndian>()?);
166                let txid = TxId::read(r)?;
167
168                ScanTarget {
169                    block_height,
170                    txid,
171                    narrow_scan_area: true,
172                }
173            })
174        })?
175        .into_iter()
176        .collect::<BTreeSet<_>>();
177
178        Ok(Self {
179            scan_ranges,
180            sapling_shard_ranges,
181            orchard_shard_ranges,
182            scan_targets,
183            initial_sync_state: InitialSyncState::new(),
184        })
185    }
186
187    /// Serialize into `writer`
188    pub fn write<W: Write>(&mut self, mut writer: W) -> std::io::Result<()> {
189        writer.write_u8(Self::serialized_version())?;
190        Vector::write(&mut writer, self.scan_ranges(), |w, scan_range| {
191            w.write_u32::<LittleEndian>(scan_range.block_range().start.into())?;
192            w.write_u32::<LittleEndian>(scan_range.block_range().end.into())?;
193            w.write_u8(scan_range.priority() as u8)
194        })?;
195        Vector::write(&mut writer, &self.sapling_shard_ranges, |w, shard_range| {
196            w.write_u32::<LittleEndian>(shard_range.start.into())?;
197            w.write_u32::<LittleEndian>(shard_range.end.into())
198        })?;
199        Vector::write(&mut writer, &self.orchard_shard_ranges, |w, shard_range| {
200            w.write_u32::<LittleEndian>(shard_range.start.into())?;
201            w.write_u32::<LittleEndian>(shard_range.end.into())
202        })?;
203        Vector::write(
204            &mut writer,
205            &self.scan_targets.iter().collect::<Vec<_>>(),
206            |w, &scan_target| scan_target.write(w),
207        )
208    }
209}
210
211impl TreeBounds {
212    fn serialized_version() -> u8 {
213        0
214    }
215
216    /// Deserialize into `reader`
217    pub fn read<R: Read>(mut reader: R) -> std::io::Result<Self> {
218        let _version = reader.read_u8()?;
219        let sapling_initial_tree_size = reader.read_u32::<LittleEndian>()?;
220        let sapling_final_tree_size = reader.read_u32::<LittleEndian>()?;
221        let orchard_initial_tree_size = reader.read_u32::<LittleEndian>()?;
222        let orchard_final_tree_size = reader.read_u32::<LittleEndian>()?;
223
224        Ok(Self {
225            sapling_initial_tree_size,
226            sapling_final_tree_size,
227            orchard_initial_tree_size,
228            orchard_final_tree_size,
229        })
230    }
231
232    /// Serialize into `writer`
233    pub fn write<W: Write>(&self, writer: &mut W) -> std::io::Result<()> {
234        writer.write_u8(Self::serialized_version())?;
235        writer.write_u32::<LittleEndian>(self.sapling_initial_tree_size)?;
236        writer.write_u32::<LittleEndian>(self.sapling_final_tree_size)?;
237        writer.write_u32::<LittleEndian>(self.orchard_initial_tree_size)?;
238        writer.write_u32::<LittleEndian>(self.orchard_final_tree_size)
239    }
240}
241
242impl NullifierMap {
243    fn serialized_version() -> u8 {
244        1
245    }
246
247    /// Deserialize into `reader`
248    pub fn read<R: Read>(mut reader: R) -> std::io::Result<Self> {
249        let version = reader.read_u8()?;
250        let sapling = Vector::read(&mut reader, |r| {
251            let mut nullifier_bytes = [0u8; 32];
252            r.read_exact(&mut nullifier_bytes)?;
253            let nullifier =
254                sapling_crypto::Nullifier::from_slice(&nullifier_bytes).map_err(|e| {
255                    std::io::Error::new(
256                        std::io::ErrorKind::InvalidData,
257                        format!("failed to read nullifier. {e}"),
258                    )
259                })?;
260            let scan_target = if version >= 1 {
261                ScanTarget::read(r)?
262            } else {
263                let block_height = BlockHeight::from_u32(r.read_u32::<LittleEndian>()?);
264                let txid = TxId::read(r)?;
265
266                ScanTarget {
267                    block_height,
268                    txid,
269                    narrow_scan_area: false,
270                }
271            };
272
273            Ok((nullifier, scan_target))
274        })?
275        .into_iter()
276        .collect::<BTreeMap<_, _>>();
277
278        let orchard = Vector::read(&mut reader, |r| {
279            let mut nullifier_bytes = [0u8; 32];
280            r.read_exact(&mut nullifier_bytes)?;
281            let nullifier = orchard::note::Nullifier::from_bytes(&nullifier_bytes)
282                .expect("nullifier bytes should be valid");
283            let scan_target = if version >= 1 {
284                ScanTarget::read(r)?
285            } else {
286                let block_height = BlockHeight::from_u32(r.read_u32::<LittleEndian>()?);
287                let txid = TxId::read(r)?;
288
289                ScanTarget {
290                    block_height,
291                    txid,
292                    narrow_scan_area: false,
293                }
294            };
295
296            Ok((nullifier, scan_target))
297        })?
298        .into_iter()
299        .collect::<BTreeMap<_, _>>();
300
301        Ok(NullifierMap { sapling, orchard })
302    }
303
304    /// Serialize into `writer`
305    pub fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
306        writer.write_u8(Self::serialized_version())?;
307        Vector::write(
308            &mut writer,
309            &self.sapling.iter().collect::<Vec<_>>(),
310            |w, &(&nullifier, &scan_target)| {
311                w.write_all(nullifier.as_ref())?;
312                scan_target.write(w)
313            },
314        )?;
315        Vector::write(
316            &mut writer,
317            &self.orchard.iter().collect::<Vec<_>>(),
318            |w, &(&nullifier, &scan_target)| {
319                w.write_all(&nullifier.to_bytes())?;
320                scan_target.write(w)
321            },
322        )
323    }
324}
325
326impl WalletBlock {
327    fn serialized_version() -> u8 {
328        0
329    }
330
331    /// Deserialize into `reader`
332    pub fn read<R: Read>(mut reader: R) -> std::io::Result<Self> {
333        let _version = reader.read_u8()?;
334        let block_height = BlockHeight::from_u32(reader.read_u32::<LittleEndian>()?);
335        let mut block_hash = BlockHash([0u8; 32]);
336        reader.read_exact(&mut block_hash.0)?;
337        let mut prev_hash = BlockHash([0u8; 32]);
338        reader.read_exact(&mut prev_hash.0)?;
339        let time = reader.read_u32::<LittleEndian>()?;
340        let txids = Vector::read(&mut reader, |r| TxId::read(r))?;
341        let tree_bounds = TreeBounds::read(&mut reader)?;
342
343        Ok(Self {
344            block_height,
345            block_hash,
346            prev_hash,
347            time,
348            txids,
349            tree_bounds,
350        })
351    }
352
353    /// Serialize into `writer`
354    pub fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
355        writer.write_u8(Self::serialized_version())?;
356        writer.write_u32::<LittleEndian>(self.block_height.into())?;
357        writer.write_all(&self.block_hash.0)?;
358        writer.write_all(&self.prev_hash.0)?;
359        writer.write_u32::<LittleEndian>(self.time)?;
360        Vector::write(&mut writer, self.txids(), |w, txid| txid.write(w))?;
361        self.tree_bounds.write(&mut writer)
362    }
363}
364
365impl WalletTransaction {
366    fn serialized_version() -> u8 {
367        0
368    }
369
370    /// Deserialize into `reader`
371    pub fn read<R: Read>(
372        mut reader: R,
373        consensus_parameters: &impl consensus::Parameters,
374    ) -> std::io::Result<Self> {
375        let _version = reader.read_u8()?;
376        let txid = TxId::read(&mut reader)?;
377        let status = ConfirmationStatus::read(&mut reader)?;
378        let transaction = Transaction::read(
379            &mut reader,
380            consensus::BranchId::for_height(consensus_parameters, status.get_height()),
381        )?;
382        let datetime = reader.read_u32::<LittleEndian>()?;
383        let transparent_coins = Vector::read(&mut reader, |r| TransparentCoin::read(r))?;
384        let sapling_notes = Vector::read(&mut reader, |r| SaplingNote::read(r))?;
385        let orchard_notes = Vector::read(&mut reader, |r| OrchardNote::read(r))?;
386        let outgoing_sapling_notes = Vector::read(&mut reader, |r| {
387            OutgoingSaplingNote::read(r, consensus_parameters)
388        })?;
389        let outgoing_orchard_notes = Vector::read(&mut reader, |r| {
390            OutgoingOrchardNote::read(r, consensus_parameters)
391        })?;
392
393        Ok(Self {
394            txid,
395            status,
396            transaction,
397            datetime,
398            transparent_coins,
399            sapling_notes,
400            orchard_notes,
401            outgoing_sapling_notes,
402            outgoing_orchard_notes,
403        })
404    }
405
406    /// Serialize into `writer`
407    pub fn write<W: Write>(
408        &self,
409        mut writer: W,
410        consensus_parameters: &impl consensus::Parameters,
411    ) -> std::io::Result<()> {
412        writer.write_u8(Self::serialized_version())?;
413        self.txid.write(&mut writer)?;
414        self.status.write(&mut writer)?;
415        self.transaction.write(&mut writer)?;
416        writer.write_u32::<LittleEndian>(self.datetime)?;
417        Vector::write(&mut writer, self.transparent_coins(), |w, output| {
418            output.write(w)
419        })?;
420        Vector::write(&mut writer, self.sapling_notes(), |w, output| {
421            output.write(w)
422        })?;
423        Vector::write(&mut writer, self.orchard_notes(), |w, output| {
424            output.write(w)
425        })?;
426        Vector::write(&mut writer, self.outgoing_sapling_notes(), |w, output| {
427            output.write(w, consensus_parameters)
428        })?;
429        Vector::write(&mut writer, self.outgoing_orchard_notes(), |w, output| {
430            output.write(w, consensus_parameters)
431        })
432    }
433}
434
435impl TransparentCoin {
436    fn serialized_version() -> u8 {
437        1
438    }
439
440    /// Deserialize into `reader`
441    pub fn read<R: Read>(mut reader: R) -> std::io::Result<Self> {
442        let version = reader.read_u8()?;
443
444        let txid = TxId::read(&mut reader)?;
445        let output_index = if version >= 1 {
446            reader.read_u32::<LittleEndian>()?
447        } else {
448            u32::from(reader.read_u16::<LittleEndian>()?)
449        };
450
451        let account_id = zip32::AccountId::try_from(reader.read_u32::<LittleEndian>()?)
452            .expect("only valid account ids written");
453        let scope = TransparentScope::try_from(reader.read_u8()?)?;
454        let address_index = reader.read_u32::<LittleEndian>()?;
455
456        let address = read_string(&mut reader)?;
457        let script = Script::read(&mut reader)?;
458        let value = Zatoshis::from_u64(reader.read_u64::<LittleEndian>()?)
459            .expect("only valid values written");
460        let spending_transaction = Optional::read(&mut reader, TxId::read)?;
461
462        Ok(Self {
463            output_id: OutputId { txid, output_index },
464            key_id: TransparentAddressId::new(
465                account_id,
466                scope,
467                NonHardenedChildIndex::from_index(address_index)
468                    .expect("only non-hardened child indexes should be written"),
469            ),
470            address,
471            value,
472            script,
473            spending_transaction,
474        })
475    }
476
477    /// Serialize into `writer`
478    pub fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
479        writer.write_u8(Self::serialized_version())?;
480
481        self.output_id.txid().write(&mut writer)?;
482        writer.write_u32::<LittleEndian>(self.output_id.output_index())?;
483
484        writer.write_u32::<LittleEndian>(self.key_id.account_id().into())?;
485        writer.write_u8(self.key_id.scope() as u8)?;
486        writer.write_u32::<LittleEndian>(self.key_id.address_index().index())?;
487
488        write_string(&mut writer, &self.address)?;
489        self.script.write(&mut writer)?;
490        writer.write_u64::<LittleEndian>(self.value())?;
491        Optional::write(&mut writer, self.spending_transaction, |w, txid| {
492            txid.write(w)
493        })?;
494
495        Ok(())
496    }
497}
498
499impl<N, Nf: Copy> WalletNote<N, Nf> {
500    fn serialized_version() -> u8 {
501        2
502    }
503}
504
505fn read_refetch_nullifier_ranges(
506    reader: &mut impl Read,
507    version: u8,
508) -> std::io::Result<Vec<Range<BlockHeight>>> {
509    if version >= 1 {
510        Vector::read(reader, |r| {
511            let start = r.read_u32::<LittleEndian>()?;
512            let end = r.read_u32::<LittleEndian>()?;
513            Ok(BlockHeight::from_u32(start)..BlockHeight::from_u32(end))
514        })
515    } else {
516        Ok(Vec::new())
517    }
518}
519
520fn write_refetch_nullifier_ranges(
521    writer: &mut impl Write,
522    ranges: &[Range<BlockHeight>],
523) -> std::io::Result<()> {
524    Vector::write(writer, ranges, |w, range| {
525        w.write_u32::<LittleEndian>(range.start.into())?;
526        w.write_u32::<LittleEndian>(range.end.into())
527    })
528}
529
530impl SaplingNote {
531    /// Deserialize into `reader`
532    pub fn read<R: Read>(mut reader: R) -> std::io::Result<Self> {
533        let version = reader.read_u8()?;
534
535        let txid = TxId::read(&mut reader)?;
536        let output_index = if version >= 2 {
537            reader.read_u32::<LittleEndian>()?
538        } else {
539            u32::from(reader.read_u16::<LittleEndian>()?)
540        };
541
542        let account_id =
543            zip32::AccountId::try_from(reader.read_u32::<LittleEndian>()?).map_err(|e| {
544                std::io::Error::new(
545                    std::io::ErrorKind::InvalidData,
546                    format!("failed to read account id. {e}"),
547                )
548            })?;
549        let scope = match reader.read_u8()? {
550            0 => Ok(zip32::Scope::External),
551            1 => Ok(zip32::Scope::Internal),
552            _ => Err(std::io::Error::new(
553                std::io::ErrorKind::InvalidData,
554                "invalid scope value",
555            )),
556        }?;
557
558        let mut address_bytes = [0u8; 43];
559        reader.read_exact(&mut address_bytes)?;
560        let recipient =
561            sapling_crypto::PaymentAddress::from_bytes(&address_bytes).ok_or_else(|| {
562                std::io::Error::new(
563                    std::io::ErrorKind::InvalidData,
564                    "failed to read payment address",
565                )
566            })?;
567        let value = sapling_crypto::value::NoteValue::from_raw(reader.read_u64::<LittleEndian>()?);
568        let rseed_zip212 = reader.read_u8()?;
569        let mut rseed_bytes = [0u8; 32];
570        reader.read_exact(&mut rseed_bytes)?;
571        let rseed = match rseed_zip212 {
572            0 => sapling_crypto::Rseed::BeforeZip212(
573                jubjub::Fr::from_bytes(&rseed_bytes).expect("should read valid jubjub bytes"),
574            ),
575            1 => sapling_crypto::Rseed::AfterZip212(rseed_bytes),
576            _ => {
577                return Err(std::io::Error::new(
578                    std::io::ErrorKind::InvalidData,
579                    "invalid rseed zip212 byte",
580                ));
581            }
582        };
583
584        let nullifier = Optional::read(&mut reader, |r| {
585            let mut nullifier_bytes = [0u8; 32];
586            r.read_exact(&mut nullifier_bytes)?;
587
588            sapling_crypto::Nullifier::from_slice(&nullifier_bytes).map_err(|e| {
589                std::io::Error::new(
590                    std::io::ErrorKind::InvalidData,
591                    format!("failed to read nullifier. {e}"),
592                )
593            })
594        })?;
595        let position = Optional::read(&mut reader, |r| {
596            Ok(Position::from(r.read_u64::<LittleEndian>()?))
597        })?;
598        let mut memo_bytes = [0u8; 512];
599        reader.read_exact(&mut memo_bytes)?;
600        let memo = Memo::from_bytes(&memo_bytes).map_err(|e| {
601            std::io::Error::new(
602                std::io::ErrorKind::InvalidData,
603                format!("failed to read memo. {e}"),
604            )
605        })?;
606
607        let spending_transaction = Optional::read(&mut reader, TxId::read)?;
608        let refetch_nullifier_ranges = read_refetch_nullifier_ranges(&mut reader, version)?;
609
610        Ok(Self {
611            output_id: OutputId::new(txid, output_index),
612            key_id: KeyId::from_parts(account_id, scope),
613            note: sapling_crypto::Note::from_parts(recipient, value, rseed),
614            nullifier,
615            position,
616            memo,
617            spending_transaction,
618            refetch_nullifier_ranges,
619        })
620    }
621
622    /// Serialize into `writer`
623    pub fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
624        writer.write_u8(Self::serialized_version())?;
625
626        self.output_id.txid().write(&mut writer)?;
627        writer.write_u32::<LittleEndian>(self.output_id.output_index())?;
628
629        writer.write_u32::<LittleEndian>(self.key_id.account_id.into())?;
630        writer.write_u8(self.key_id.scope as u8)?;
631
632        writer.write_all(&self.note.recipient().to_bytes())?;
633        writer.write_u64::<LittleEndian>(self.value())?;
634        match self.note.rseed() {
635            sapling_crypto::Rseed::BeforeZip212(fr) => {
636                writer.write_u8(0)?;
637                writer.write_all(&fr.to_bytes())?;
638            }
639            sapling_crypto::Rseed::AfterZip212(bytes) => {
640                writer.write_u8(1)?;
641                writer.write_all(bytes)?;
642            }
643        }
644
645        Optional::write(&mut writer, self.nullifier, |w, nullifier| {
646            w.write_all(nullifier.as_ref())
647        })?;
648        Optional::write(&mut writer, self.position, |w, position| {
649            w.write_u64::<LittleEndian>(position.into())
650        })?;
651        writer.write_all(self.memo.encode().as_array())?;
652
653        Optional::write(&mut writer, self.spending_transaction, |w, txid| {
654            txid.write(w)
655        })?;
656
657        write_refetch_nullifier_ranges(&mut writer, &self.refetch_nullifier_ranges)
658    }
659}
660
661impl OrchardNote {
662    /// Deserialize into `reader`
663    pub fn read<R: Read>(mut reader: R) -> std::io::Result<Self> {
664        let version = reader.read_u8()?;
665
666        let txid = TxId::read(&mut reader)?;
667        let output_index = if version >= 2 {
668            reader.read_u32::<LittleEndian>()?
669        } else {
670            u32::from(reader.read_u16::<LittleEndian>()?)
671        };
672
673        let account_id =
674            zip32::AccountId::try_from(reader.read_u32::<LittleEndian>()?).map_err(|e| {
675                std::io::Error::new(
676                    std::io::ErrorKind::InvalidData,
677                    format!("failed to read account id. {e}"),
678                )
679            })?;
680        let scope = match reader.read_u8()? {
681            0 => Ok(zip32::Scope::External),
682            1 => Ok(zip32::Scope::Internal),
683            _ => Err(std::io::Error::new(
684                std::io::ErrorKind::InvalidData,
685                "invalid scope value",
686            )),
687        }?;
688
689        let mut address_bytes = [0u8; 43];
690        reader.read_exact(&mut address_bytes)?;
691        let recipient = orchard::Address::from_raw_address_bytes(&address_bytes)
692            .expect("should be a valid address");
693        let value = orchard::value::NoteValue::from_raw(reader.read_u64::<LittleEndian>()?);
694        let mut rho_bytes = [0u8; 32];
695        reader.read_exact(&mut rho_bytes)?;
696        let rho = orchard::note::Rho::from_bytes(&rho_bytes).expect("should be valid rho bytes");
697        let mut rseed_bytes = [0u8; 32];
698        reader.read_exact(&mut rseed_bytes)?;
699        let rseed = orchard::note::RandomSeed::from_bytes(rseed_bytes, &rho)
700            .expect("should be valid random seed bytes");
701
702        let nullifier = Optional::read(&mut reader, |r| {
703            let mut nullifier_bytes = [0u8; 32];
704            r.read_exact(&mut nullifier_bytes)?;
705
706            Ok(orchard::note::Nullifier::from_bytes(&nullifier_bytes)
707                .expect("should be valid nullfiier bytes"))
708        })?;
709        let position = Optional::read(&mut reader, |r| {
710            Ok(Position::from(r.read_u64::<LittleEndian>()?))
711        })?;
712        let mut memo_bytes = [0u8; 512];
713        reader.read_exact(&mut memo_bytes)?;
714        let memo = Memo::from_bytes(&memo_bytes).map_err(|e| {
715            std::io::Error::new(
716                std::io::ErrorKind::InvalidData,
717                format!("failed to read memo. {e}"),
718            )
719        })?;
720
721        let spending_transaction = Optional::read(&mut reader, TxId::read)?;
722        let refetch_nullifier_ranges = read_refetch_nullifier_ranges(&mut reader, version)?;
723
724        Ok(Self {
725            output_id: OutputId::new(txid, output_index),
726            key_id: KeyId::from_parts(account_id, scope),
727            note: orchard::note::Note::from_parts(recipient, value, rho, rseed)
728                .expect("should be a valid orchard note"),
729            nullifier,
730            position,
731            memo,
732            spending_transaction,
733            refetch_nullifier_ranges,
734        })
735    }
736
737    /// Serialize into `writer`
738    pub fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
739        writer.write_u8(Self::serialized_version())?;
740
741        self.output_id.txid().write(&mut writer)?;
742        writer.write_u32::<LittleEndian>(self.output_id.output_index())?;
743
744        writer.write_u32::<LittleEndian>(self.key_id.account_id.into())?;
745        writer.write_u8(self.key_id.scope as u8)?;
746
747        writer.write_all(&self.note.recipient().to_raw_address_bytes())?;
748        writer.write_u64::<LittleEndian>(self.value())?;
749        writer.write_all(&self.note.rho().to_bytes())?;
750        writer.write_all(self.note.rseed().as_bytes())?;
751
752        Optional::write(&mut writer, self.nullifier, |w, nullifier| {
753            w.write_all(&nullifier.to_bytes())
754        })?;
755        Optional::write(&mut writer, self.position, |w, position| {
756            w.write_u64::<LittleEndian>(position.into())
757        })?;
758        writer.write_all(self.memo.encode().as_array())?;
759        Optional::write(&mut writer, self.spending_transaction, |w, txid| {
760            txid.write(w)
761        })?;
762
763        write_refetch_nullifier_ranges(&mut writer, &self.refetch_nullifier_ranges)
764    }
765}
766
767impl<N> OutgoingNote<N> {
768    fn serialized_version() -> u8 {
769        1
770    }
771}
772
773impl OutgoingSaplingNote {
774    /// Deserialize into `reader`
775    pub fn read<R: Read>(
776        mut reader: R,
777        consensus_parameters: &impl consensus::Parameters,
778    ) -> std::io::Result<Self> {
779        let version = reader.read_u8()?;
780
781        let txid = TxId::read(&mut reader)?;
782        let output_index = if version >= 1 {
783            reader.read_u32::<LittleEndian>()?
784        } else {
785            u32::from(reader.read_u16::<LittleEndian>()?)
786        };
787
788        let account_id =
789            zip32::AccountId::try_from(reader.read_u32::<LittleEndian>()?).map_err(|e| {
790                std::io::Error::new(
791                    std::io::ErrorKind::InvalidData,
792                    format!("failed to read account id. {e}"),
793                )
794            })?;
795        let scope = match reader.read_u8()? {
796            0 => Ok(zip32::Scope::External),
797            1 => Ok(zip32::Scope::Internal),
798            _ => Err(std::io::Error::new(
799                std::io::ErrorKind::InvalidData,
800                "invalid scope value",
801            )),
802        }?;
803
804        let mut address_bytes = [0u8; 43];
805        reader.read_exact(&mut address_bytes)?;
806        let recipient =
807            sapling_crypto::PaymentAddress::from_bytes(&address_bytes).ok_or_else(|| {
808                std::io::Error::new(
809                    std::io::ErrorKind::InvalidData,
810                    "failed to read payment address",
811                )
812            })?;
813        let value = sapling_crypto::value::NoteValue::from_raw(reader.read_u64::<LittleEndian>()?);
814        let rseed_zip212 = reader.read_u8()?;
815        let mut rseed_bytes = [0u8; 32];
816        reader.read_exact(&mut rseed_bytes)?;
817        let rseed = match rseed_zip212 {
818            0 => sapling_crypto::Rseed::BeforeZip212(
819                jubjub::Fr::from_bytes(&rseed_bytes).expect("should read valid jubjub bytes"),
820            ),
821            1 => sapling_crypto::Rseed::AfterZip212(rseed_bytes),
822            _ => {
823                return Err(std::io::Error::new(
824                    std::io::ErrorKind::InvalidData,
825                    "invalid rseed zip212 byte",
826                ));
827            }
828        };
829
830        let mut memo_bytes = [0u8; 512];
831        reader.read_exact(&mut memo_bytes)?;
832        let memo = Memo::from_bytes(&memo_bytes).map_err(|e| {
833            std::io::Error::new(
834                std::io::ErrorKind::InvalidData,
835                format!("failed to read memo. {e}"),
836            )
837        })?;
838
839        let recipient_unified_address = Optional::read(&mut reader, |r| {
840            let encoded_address = read_string(r)?;
841
842            decode_unified_address(consensus_parameters, &encoded_address)
843        })?;
844
845        Ok(Self {
846            output_id: OutputId::new(txid, output_index),
847            key_id: KeyId::from_parts(account_id, scope),
848            note: sapling_crypto::Note::from_parts(recipient, value, rseed),
849            memo,
850            recipient_full_unified_address: recipient_unified_address,
851        })
852    }
853
854    /// Serialize into `writer`
855    pub fn write<W: Write>(
856        &self,
857        mut writer: W,
858        consensus_parameters: &impl consensus::Parameters,
859    ) -> std::io::Result<()> {
860        writer.write_u8(Self::serialized_version())?;
861
862        self.output_id.txid().write(&mut writer)?;
863        writer.write_u32::<LittleEndian>(self.output_id.output_index())?;
864
865        writer.write_u32::<LittleEndian>(self.key_id.account_id.into())?;
866        writer.write_u8(self.key_id.scope as u8)?;
867
868        writer.write_all(&self.note.recipient().to_bytes())?;
869        writer.write_u64::<LittleEndian>(self.value())?;
870        match self.note.rseed() {
871            sapling_crypto::Rseed::BeforeZip212(fr) => {
872                writer.write_u8(0)?;
873                writer.write_all(&fr.to_bytes())?;
874            }
875            sapling_crypto::Rseed::AfterZip212(bytes) => {
876                writer.write_u8(1)?;
877                writer.write_all(bytes)?;
878            }
879        }
880
881        writer.write_all(self.memo.encode().as_array())?;
882        Optional::write(
883            &mut writer,
884            self.recipient_full_unified_address.as_ref(),
885            |w, unified_address| write_string(w, &unified_address.encode(consensus_parameters)),
886        )?;
887
888        Ok(())
889    }
890}
891
892impl OutgoingOrchardNote {
893    /// Deserialize into `reader`
894    pub fn read<R: Read>(
895        mut reader: R,
896        consensus_parameters: &impl consensus::Parameters,
897    ) -> std::io::Result<Self> {
898        let version = reader.read_u8()?;
899
900        let txid = TxId::read(&mut reader)?;
901        let output_index = if version >= 1 {
902            reader.read_u32::<LittleEndian>()?
903        } else {
904            u32::from(reader.read_u16::<LittleEndian>()?)
905        };
906
907        let account_id =
908            zip32::AccountId::try_from(reader.read_u32::<LittleEndian>()?).map_err(|e| {
909                std::io::Error::new(
910                    std::io::ErrorKind::InvalidData,
911                    format!("failed to read account id. {e}"),
912                )
913            })?;
914        let scope = match reader.read_u8()? {
915            0 => Ok(zip32::Scope::External),
916            1 => Ok(zip32::Scope::Internal),
917            _ => Err(std::io::Error::new(
918                std::io::ErrorKind::InvalidData,
919                "invalid scope value",
920            )),
921        }?;
922
923        let mut address_bytes = [0u8; 43];
924        reader.read_exact(&mut address_bytes)?;
925        let recipient = orchard::Address::from_raw_address_bytes(&address_bytes)
926            .expect("should be a valid address");
927        let value = orchard::value::NoteValue::from_raw(reader.read_u64::<LittleEndian>()?);
928        let mut rho_bytes = [0u8; 32];
929        reader.read_exact(&mut rho_bytes)?;
930        let rho = orchard::note::Rho::from_bytes(&rho_bytes).expect("should be valid rho bytes");
931        let mut rseed_bytes = [0u8; 32];
932        reader.read_exact(&mut rseed_bytes)?;
933        let rseed = orchard::note::RandomSeed::from_bytes(rseed_bytes, &rho)
934            .expect("should be valid random seed bytes");
935
936        let mut memo_bytes = [0u8; 512];
937        reader.read_exact(&mut memo_bytes)?;
938        let memo = Memo::from_bytes(&memo_bytes).map_err(|e| {
939            std::io::Error::new(
940                std::io::ErrorKind::InvalidData,
941                format!("failed to read memo. {e}"),
942            )
943        })?;
944
945        let recipient_unified_address = Optional::read(&mut reader, |r| {
946            let encoded_address = read_string(r)?;
947
948            decode_unified_address(consensus_parameters, &encoded_address)
949        })?;
950
951        Ok(Self {
952            output_id: OutputId::new(txid, output_index),
953            key_id: KeyId::from_parts(account_id, scope),
954            note: orchard::note::Note::from_parts(recipient, value, rho, rseed)
955                .expect("should be a valid orchard note"),
956            memo,
957            recipient_full_unified_address: recipient_unified_address,
958        })
959    }
960
961    /// Serialize into `writer`
962    pub fn write<W: Write>(
963        &self,
964        mut writer: W,
965        consensus_parameters: &impl consensus::Parameters,
966    ) -> std::io::Result<()> {
967        writer.write_u8(Self::serialized_version())?;
968
969        self.output_id.txid().write(&mut writer)?;
970        writer.write_u32::<LittleEndian>(self.output_id.output_index())?;
971
972        writer.write_u32::<LittleEndian>(self.key_id.account_id.into())?;
973        writer.write_u8(self.key_id.scope as u8)?;
974
975        writer.write_all(&self.note.recipient().to_raw_address_bytes())?;
976        writer.write_u64::<LittleEndian>(self.value())?;
977        writer.write_all(&self.note.rho().to_bytes())?;
978        writer.write_all(self.note.rseed().as_bytes())?;
979
980        writer.write_all(self.memo.encode().as_array())?;
981        Optional::write(
982            &mut writer,
983            self.recipient_full_unified_address.as_ref(),
984            |w, unified_address| write_string(w, &unified_address.encode(consensus_parameters)),
985        )?;
986
987        Ok(())
988    }
989}
990
991impl ShardTrees {
992    fn serialized_version() -> u8 {
993        0
994    }
995
996    /// Deserialize into `reader`
997    pub fn read<R: Read>(mut reader: R) -> std::io::Result<Self> {
998        let _version = reader.read_u8()?;
999        let sapling = Self::read_shardtree(&mut reader)?;
1000        let orchard = Self::read_shardtree(&mut reader)?;
1001
1002        Ok(Self { sapling, orchard })
1003    }
1004
1005    /// Serialize into `writer`
1006    pub fn write<W: Write>(&mut self, mut writer: W) -> std::io::Result<()> {
1007        writer.write_u8(Self::serialized_version())?;
1008        Self::write_shardtree(&mut writer, &mut self.sapling)?;
1009        Self::write_shardtree(&mut writer, &mut self.orchard)?;
1010
1011        Ok(())
1012    }
1013
1014    fn read_shardtree<
1015        H: Hashable + Clone + HashSer + Eq,
1016        C: Ord + std::fmt::Debug + Copy + From<u32>,
1017        R: Read,
1018        const DEPTH: u8,
1019        const SHARD_HEIGHT: u8,
1020    >(
1021        mut reader: R,
1022    ) -> std::io::Result<ShardTree<MemoryShardStore<H, C>, DEPTH, SHARD_HEIGHT>> {
1023        let shards = Vector::read(&mut reader, |r| {
1024            let level = incrementalmerkletree::Level::from(r.read_u8()?);
1025            let index = r.read_u64::<LittleEndian>()?;
1026            let root_addr = incrementalmerkletree::Address::from_parts(level, index);
1027            let shard = read_shard(r)?;
1028
1029            LocatedPrunableTree::from_parts(root_addr, shard).map_err(|addr| {
1030                std::io::Error::new(
1031                    std::io::ErrorKind::InvalidData,
1032                    format!("parent node in root has level 0 relative to root address: {addr:?}"),
1033                )
1034            })
1035        })?;
1036        let mut store = MemoryShardStore::empty();
1037        for shard in shards {
1038            store.put_shard(shard).expect("infallible");
1039        }
1040        let checkpoints = Vector::read(&mut reader, |r| {
1041            let checkpoint_id = C::from(r.read_u32::<LittleEndian>()?);
1042            let tree_state = match r.read_u8()? {
1043                0 => TreeState::Empty,
1044                1 => TreeState::AtPosition(Position::from(r.read_u64::<LittleEndian>()?)),
1045                otherwise => {
1046                    return Err(std::io::Error::new(
1047                        std::io::ErrorKind::InvalidData,
1048                        format!(
1049                            "failed to read TreeState. expected boolean value, found {otherwise}"
1050                        ),
1051                    ));
1052                }
1053            };
1054            let marks_removed =
1055                Vector::read(r, |r| r.read_u64::<LittleEndian>().map(Position::from))?;
1056            Ok((
1057                checkpoint_id,
1058                Checkpoint::from_parts(tree_state, marks_removed.into_iter().collect()),
1059            ))
1060        })?;
1061        for (checkpoint_id, checkpoint) in checkpoints {
1062            store
1063                .add_checkpoint(checkpoint_id, checkpoint)
1064                .expect("Infallible");
1065        }
1066        store.put_cap(read_shard(reader)?).expect("Infallible");
1067
1068        Ok(shardtree::ShardTree::new(
1069            store,
1070            MAX_REORG_ALLOWANCE as usize,
1071        ))
1072    }
1073
1074    /// Write memory-backed shardstore, represented tree.
1075    fn write_shardtree<
1076        H: Hashable + Clone + Eq + HashSer,
1077        C: Ord + std::fmt::Debug + Copy,
1078        W: Write,
1079        const DEPTH: u8,
1080        const SHARD_HEIGHT: u8,
1081    >(
1082        mut writer: W,
1083        shardtree: &mut ShardTree<MemoryShardStore<H, C>, DEPTH, SHARD_HEIGHT>,
1084    ) -> std::io::Result<()>
1085    where
1086        u32: From<C>,
1087    {
1088        fn write_shards<W, H, C>(
1089            mut writer: W,
1090            store: &MemoryShardStore<H, C>,
1091        ) -> std::io::Result<()>
1092        where
1093            H: Hashable + Clone + Eq + HashSer,
1094            C: Ord + std::fmt::Debug + Copy,
1095            W: Write,
1096        {
1097            let roots = store.get_shard_roots().expect("Infallible");
1098            Vector::write(&mut writer, &roots, |w, root| {
1099                w.write_u8(root.level().into())?;
1100                w.write_u64::<LittleEndian>(root.index())?;
1101                let shard = store
1102                    .get_shard(*root)
1103                    .expect("Infallible")
1104                    .expect("cannot find root that shard store claims to have");
1105                write_shard(w, shard.root())
1106            })
1107        }
1108
1109        fn write_checkpoints<W, Cid>(
1110            mut writer: W,
1111            checkpoints: &[(Cid, Checkpoint)],
1112        ) -> std::io::Result<()>
1113        where
1114            W: Write,
1115            Cid: Ord + std::fmt::Debug + Copy,
1116            u32: From<Cid>,
1117        {
1118            Vector::write(
1119                &mut writer,
1120                checkpoints,
1121                |mut w, (checkpoint_id, checkpoint)| {
1122                    w.write_u32::<LittleEndian>(u32::from(*checkpoint_id))?;
1123                    match checkpoint.tree_state() {
1124                        shardtree::store::TreeState::Empty => w.write_u8(0),
1125                        shardtree::store::TreeState::AtPosition(pos) => {
1126                            w.write_u8(1)?;
1127                            w.write_u64::<LittleEndian>(<u64 as From<Position>>::from(pos))
1128                        }
1129                    }?;
1130                    Vector::write(
1131                        &mut w,
1132                        &checkpoint.marks_removed().iter().collect::<Vec<_>>(),
1133                        |w, mark| {
1134                            w.write_u64::<LittleEndian>(<u64 as From<Position>>::from(**mark))
1135                        },
1136                    )
1137                },
1138            )
1139        }
1140
1141        // Replace original tree with empty tree, and mutate new version into store.
1142        let mut store = std::mem::replace(
1143            shardtree,
1144            shardtree::ShardTree::new(MemoryShardStore::empty(), 0),
1145        )
1146        .into_store();
1147
1148        macro_rules! write_with_error_handling {
1149            ($writer: ident, $from: ident) => {
1150                if let Err(e) = $writer(&mut writer, &$from) {
1151                    *shardtree = shardtree::ShardTree::new(store, MAX_REORG_ALLOWANCE as usize);
1152                    return Err(e);
1153                }
1154            };
1155        }
1156
1157        // Write located prunable trees
1158        write_with_error_handling!(write_shards, store);
1159
1160        // Write checkpoints
1161        let mut checkpoints = Vec::new();
1162        let checkpoint_count = store.checkpoint_count().expect("Infallible");
1163        store
1164            .with_checkpoints(checkpoint_count, |checkpoint_id, checkpoint| {
1165                checkpoints.push((*checkpoint_id, checkpoint.clone()));
1166                Ok(())
1167            })
1168            .expect("Infallible");
1169        if checkpoints.len() > MAX_REORG_ALLOWANCE as usize {
1170            let keep_from = checkpoints.len() - MAX_REORG_ALLOWANCE as usize;
1171            checkpoints.drain(..keep_from);
1172        }
1173        write_with_error_handling!(write_checkpoints, checkpoints);
1174
1175        // Write cap
1176        let cap = store.get_cap().expect("Infallible");
1177        write_with_error_handling!(write_shard, cap);
1178
1179        *shardtree = shardtree::ShardTree::new(store, MAX_REORG_ALLOWANCE as usize);
1180
1181        Ok(())
1182    }
1183}
1184
1185#[cfg(test)]
1186mod tests {
1187    use super::*;
1188
1189    #[test]
1190    fn shardtree_roundtrip_keeps_newest_checkpoints() {
1191        let mut shard_trees = ShardTrees::new();
1192
1193        for height in 1..=150 {
1194            let height = BlockHeight::from_u32(height);
1195            shard_trees
1196                .sapling
1197                .store_mut()
1198                .add_checkpoint(
1199                    height,
1200                    Checkpoint::from_parts(TreeState::Empty, BTreeSet::new()),
1201                )
1202                .expect("infallible");
1203            shard_trees
1204                .orchard
1205                .store_mut()
1206                .add_checkpoint(
1207                    height,
1208                    Checkpoint::from_parts(TreeState::Empty, BTreeSet::new()),
1209                )
1210                .expect("infallible");
1211        }
1212
1213        let mut bytes = Vec::new();
1214        shard_trees.write(&mut bytes).expect("write should succeed");
1215        let roundtripped = ShardTrees::read(bytes.as_slice()).expect("read should succeed");
1216
1217        let sapling_store = roundtripped.sapling.store();
1218        let orchard_store = roundtripped.orchard.store();
1219
1220        assert_eq!(sapling_store.checkpoint_count().expect("infallible"), 100);
1221        assert_eq!(orchard_store.checkpoint_count().expect("infallible"), 100);
1222        assert_eq!(
1223            sapling_store.min_checkpoint_id().expect("infallible"),
1224            Some(BlockHeight::from_u32(51))
1225        );
1226        assert_eq!(
1227            sapling_store.max_checkpoint_id().expect("infallible"),
1228            Some(BlockHeight::from_u32(150))
1229        );
1230        assert_eq!(
1231            orchard_store.min_checkpoint_id().expect("infallible"),
1232            Some(BlockHeight::from_u32(51))
1233        );
1234        assert_eq!(
1235            orchard_store.max_checkpoint_id().expect("infallible"),
1236            Some(BlockHeight::from_u32(150))
1237        );
1238        assert!(
1239            sapling_store
1240                .get_checkpoint(&BlockHeight::from_u32(149))
1241                .expect("infallible")
1242                .is_some()
1243        );
1244        assert!(
1245            sapling_store
1246                .get_checkpoint(&BlockHeight::from_u32(50))
1247                .expect("infallible")
1248                .is_none()
1249        );
1250    }
1251}