1use 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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_with_error_handling!(write_shards, store);
1159
1160 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 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}