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 memo::Memo,
21 merkle_tree::HashSer,
22 transaction::{Transaction, TxId},
23};
24use zcash_protocol::{
25 consensus::{self, BlockHeight},
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 0
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 = reader.read_u16::<LittleEndian>()?;
446
447 let account_id = zip32::AccountId::try_from(reader.read_u32::<LittleEndian>()?)
448 .expect("only valid account ids written");
449 let scope = TransparentScope::try_from(reader.read_u8()?)?;
450 let address_index = reader.read_u32::<LittleEndian>()?;
451
452 let address = read_string(&mut reader)?;
453 let script = Script::read(&mut reader)?;
454 let value = Zatoshis::from_u64(reader.read_u64::<LittleEndian>()?)
455 .expect("only valid values written");
456 let spending_transaction = Optional::read(&mut reader, TxId::read)?;
457
458 Ok(Self {
459 output_id: OutputId { txid, output_index },
460 key_id: TransparentAddressId::new(
461 account_id,
462 scope,
463 NonHardenedChildIndex::from_index(address_index)
464 .expect("only non-hardened child indexes should be written"),
465 ),
466 address,
467 value,
468 script,
469 spending_transaction,
470 })
471 }
472
473 pub fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
475 writer.write_u8(Self::serialized_version())?;
476
477 self.output_id.txid().write(&mut writer)?;
478 writer.write_u16::<LittleEndian>(self.output_id.output_index())?;
479
480 writer.write_u32::<LittleEndian>(self.key_id.account_id().into())?;
481 writer.write_u8(self.key_id.scope() as u8)?;
482 writer.write_u32::<LittleEndian>(self.key_id.address_index().index())?;
483
484 write_string(&mut writer, &self.address)?;
485 self.script.write(&mut writer)?;
486 writer.write_u64::<LittleEndian>(self.value())?;
487 Optional::write(&mut writer, self.spending_transaction, |w, txid| {
488 txid.write(w)
489 })?;
490
491 Ok(())
492 }
493}
494
495impl<N, Nf: Copy> WalletNote<N, Nf> {
496 fn serialized_version() -> u8 {
497 1
498 }
499}
500
501fn read_refetch_nullifier_ranges(
502 reader: &mut impl Read,
503 version: u8,
504) -> std::io::Result<Vec<Range<BlockHeight>>> {
505 if version >= 1 {
506 Vector::read(reader, |r| {
507 let start = r.read_u32::<LittleEndian>()?;
508 let end = r.read_u32::<LittleEndian>()?;
509 Ok(BlockHeight::from_u32(start)..BlockHeight::from_u32(end))
510 })
511 } else {
512 Ok(Vec::new())
513 }
514}
515
516fn write_refetch_nullifier_ranges(
517 writer: &mut impl Write,
518 ranges: &[Range<BlockHeight>],
519) -> std::io::Result<()> {
520 Vector::write(writer, ranges, |w, range| {
521 w.write_u32::<LittleEndian>(range.start.into())?;
522 w.write_u32::<LittleEndian>(range.end.into())
523 })
524}
525
526impl SaplingNote {
527 pub fn read<R: Read>(mut reader: R) -> std::io::Result<Self> {
529 let version = reader.read_u8()?;
530
531 let txid = TxId::read(&mut reader)?;
532 let output_index = reader.read_u16::<LittleEndian>()?;
533
534 let account_id =
535 zip32::AccountId::try_from(reader.read_u32::<LittleEndian>()?).map_err(|e| {
536 std::io::Error::new(
537 std::io::ErrorKind::InvalidData,
538 format!("failed to read account id. {e}"),
539 )
540 })?;
541 let scope = match reader.read_u8()? {
542 0 => Ok(zip32::Scope::External),
543 1 => Ok(zip32::Scope::Internal),
544 _ => Err(std::io::Error::new(
545 std::io::ErrorKind::InvalidData,
546 "invalid scope value",
547 )),
548 }?;
549
550 let mut address_bytes = [0u8; 43];
551 reader.read_exact(&mut address_bytes)?;
552 let recipient =
553 sapling_crypto::PaymentAddress::from_bytes(&address_bytes).ok_or_else(|| {
554 std::io::Error::new(
555 std::io::ErrorKind::InvalidData,
556 "failed to read payment address",
557 )
558 })?;
559 let value = sapling_crypto::value::NoteValue::from_raw(reader.read_u64::<LittleEndian>()?);
560 let rseed_zip212 = reader.read_u8()?;
561 let mut rseed_bytes = [0u8; 32];
562 reader.read_exact(&mut rseed_bytes)?;
563 let rseed = match rseed_zip212 {
564 0 => sapling_crypto::Rseed::BeforeZip212(
565 jubjub::Fr::from_bytes(&rseed_bytes).expect("should read valid jubjub bytes"),
566 ),
567 1 => sapling_crypto::Rseed::AfterZip212(rseed_bytes),
568 _ => {
569 return Err(std::io::Error::new(
570 std::io::ErrorKind::InvalidData,
571 "invalid rseed zip212 byte",
572 ));
573 }
574 };
575
576 let nullifier = Optional::read(&mut reader, |r| {
577 let mut nullifier_bytes = [0u8; 32];
578 r.read_exact(&mut nullifier_bytes)?;
579
580 sapling_crypto::Nullifier::from_slice(&nullifier_bytes).map_err(|e| {
581 std::io::Error::new(
582 std::io::ErrorKind::InvalidData,
583 format!("failed to read nullifier. {e}"),
584 )
585 })
586 })?;
587 let position = Optional::read(&mut reader, |r| {
588 Ok(Position::from(r.read_u64::<LittleEndian>()?))
589 })?;
590 let mut memo_bytes = [0u8; 512];
591 reader.read_exact(&mut memo_bytes)?;
592 let memo = Memo::from_bytes(&memo_bytes).map_err(|e| {
593 std::io::Error::new(
594 std::io::ErrorKind::InvalidData,
595 format!("failed to read memo. {e}"),
596 )
597 })?;
598
599 let spending_transaction = Optional::read(&mut reader, TxId::read)?;
600 let refetch_nullifier_ranges = read_refetch_nullifier_ranges(&mut reader, version)?;
601
602 Ok(Self {
603 output_id: OutputId::new(txid, output_index),
604 key_id: KeyId::from_parts(account_id, scope),
605 note: sapling_crypto::Note::from_parts(recipient, value, rseed),
606 nullifier,
607 position,
608 memo,
609 spending_transaction,
610 refetch_nullifier_ranges,
611 })
612 }
613
614 pub fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
616 writer.write_u8(Self::serialized_version())?;
617
618 self.output_id.txid().write(&mut writer)?;
619 writer.write_u16::<LittleEndian>(self.output_id.output_index())?;
620
621 writer.write_u32::<LittleEndian>(self.key_id.account_id.into())?;
622 writer.write_u8(self.key_id.scope as u8)?;
623
624 writer.write_all(&self.note.recipient().to_bytes())?;
625 writer.write_u64::<LittleEndian>(self.value())?;
626 match self.note.rseed() {
627 sapling_crypto::Rseed::BeforeZip212(fr) => {
628 writer.write_u8(0)?;
629 writer.write_all(&fr.to_bytes())?;
630 }
631 sapling_crypto::Rseed::AfterZip212(bytes) => {
632 writer.write_u8(1)?;
633 writer.write_all(bytes)?;
634 }
635 }
636
637 Optional::write(&mut writer, self.nullifier, |w, nullifier| {
638 w.write_all(nullifier.as_ref())
639 })?;
640 Optional::write(&mut writer, self.position, |w, position| {
641 w.write_u64::<LittleEndian>(position.into())
642 })?;
643 writer.write_all(self.memo.encode().as_array())?;
644
645 Optional::write(&mut writer, self.spending_transaction, |w, txid| {
646 txid.write(w)
647 })?;
648
649 write_refetch_nullifier_ranges(&mut writer, &self.refetch_nullifier_ranges)
650 }
651}
652
653impl OrchardNote {
654 pub fn read<R: Read>(mut reader: R) -> std::io::Result<Self> {
656 let version = reader.read_u8()?;
657
658 let txid = TxId::read(&mut reader)?;
659 let output_index = reader.read_u16::<LittleEndian>()?;
660
661 let account_id =
662 zip32::AccountId::try_from(reader.read_u32::<LittleEndian>()?).map_err(|e| {
663 std::io::Error::new(
664 std::io::ErrorKind::InvalidData,
665 format!("failed to read account id. {e}"),
666 )
667 })?;
668 let scope = match reader.read_u8()? {
669 0 => Ok(zip32::Scope::External),
670 1 => Ok(zip32::Scope::Internal),
671 _ => Err(std::io::Error::new(
672 std::io::ErrorKind::InvalidData,
673 "invalid scope value",
674 )),
675 }?;
676
677 let mut address_bytes = [0u8; 43];
678 reader.read_exact(&mut address_bytes)?;
679 let recipient = orchard::Address::from_raw_address_bytes(&address_bytes)
680 .expect("should be a valid address");
681 let value = orchard::value::NoteValue::from_raw(reader.read_u64::<LittleEndian>()?);
682 let mut rho_bytes = [0u8; 32];
683 reader.read_exact(&mut rho_bytes)?;
684 let rho = orchard::note::Rho::from_bytes(&rho_bytes).expect("should be valid rho bytes");
685 let mut rseed_bytes = [0u8; 32];
686 reader.read_exact(&mut rseed_bytes)?;
687 let rseed = orchard::note::RandomSeed::from_bytes(rseed_bytes, &rho)
688 .expect("should be valid random seed bytes");
689
690 let nullifier = Optional::read(&mut reader, |r| {
691 let mut nullifier_bytes = [0u8; 32];
692 r.read_exact(&mut nullifier_bytes)?;
693
694 Ok(orchard::note::Nullifier::from_bytes(&nullifier_bytes)
695 .expect("should be valid nullfiier bytes"))
696 })?;
697 let position = Optional::read(&mut reader, |r| {
698 Ok(Position::from(r.read_u64::<LittleEndian>()?))
699 })?;
700 let mut memo_bytes = [0u8; 512];
701 reader.read_exact(&mut memo_bytes)?;
702 let memo = Memo::from_bytes(&memo_bytes).map_err(|e| {
703 std::io::Error::new(
704 std::io::ErrorKind::InvalidData,
705 format!("failed to read memo. {e}"),
706 )
707 })?;
708
709 let spending_transaction = Optional::read(&mut reader, TxId::read)?;
710 let refetch_nullifier_ranges = read_refetch_nullifier_ranges(&mut reader, version)?;
711
712 Ok(Self {
713 output_id: OutputId::new(txid, output_index),
714 key_id: KeyId::from_parts(account_id, scope),
715 note: orchard::note::Note::from_parts(recipient, value, rho, rseed)
716 .expect("should be a valid orchard note"),
717 nullifier,
718 position,
719 memo,
720 spending_transaction,
721 refetch_nullifier_ranges,
722 })
723 }
724
725 pub fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
727 writer.write_u8(Self::serialized_version())?;
728
729 self.output_id.txid().write(&mut writer)?;
730 writer.write_u16::<LittleEndian>(self.output_id.output_index())?;
731
732 writer.write_u32::<LittleEndian>(self.key_id.account_id.into())?;
733 writer.write_u8(self.key_id.scope as u8)?;
734
735 writer.write_all(&self.note.recipient().to_raw_address_bytes())?;
736 writer.write_u64::<LittleEndian>(self.value())?;
737 writer.write_all(&self.note.rho().to_bytes())?;
738 writer.write_all(self.note.rseed().as_bytes())?;
739
740 Optional::write(&mut writer, self.nullifier, |w, nullifier| {
741 w.write_all(&nullifier.to_bytes())
742 })?;
743 Optional::write(&mut writer, self.position, |w, position| {
744 w.write_u64::<LittleEndian>(position.into())
745 })?;
746 writer.write_all(self.memo.encode().as_array())?;
747 Optional::write(&mut writer, self.spending_transaction, |w, txid| {
748 txid.write(w)
749 })?;
750
751 write_refetch_nullifier_ranges(&mut writer, &self.refetch_nullifier_ranges)
752 }
753}
754
755impl<N> OutgoingNote<N> {
756 fn serialized_version() -> u8 {
757 0
758 }
759}
760
761impl OutgoingSaplingNote {
762 pub fn read<R: Read>(
764 mut reader: R,
765 consensus_parameters: &impl consensus::Parameters,
766 ) -> std::io::Result<Self> {
767 let _version = reader.read_u8()?;
768
769 let txid = TxId::read(&mut reader)?;
770 let output_index = reader.read_u16::<LittleEndian>()?;
771
772 let account_id =
773 zip32::AccountId::try_from(reader.read_u32::<LittleEndian>()?).map_err(|e| {
774 std::io::Error::new(
775 std::io::ErrorKind::InvalidData,
776 format!("failed to read account id. {e}"),
777 )
778 })?;
779 let scope = match reader.read_u8()? {
780 0 => Ok(zip32::Scope::External),
781 1 => Ok(zip32::Scope::Internal),
782 _ => Err(std::io::Error::new(
783 std::io::ErrorKind::InvalidData,
784 "invalid scope value",
785 )),
786 }?;
787
788 let mut address_bytes = [0u8; 43];
789 reader.read_exact(&mut address_bytes)?;
790 let recipient =
791 sapling_crypto::PaymentAddress::from_bytes(&address_bytes).ok_or_else(|| {
792 std::io::Error::new(
793 std::io::ErrorKind::InvalidData,
794 "failed to read payment address",
795 )
796 })?;
797 let value = sapling_crypto::value::NoteValue::from_raw(reader.read_u64::<LittleEndian>()?);
798 let rseed_zip212 = reader.read_u8()?;
799 let mut rseed_bytes = [0u8; 32];
800 reader.read_exact(&mut rseed_bytes)?;
801 let rseed = match rseed_zip212 {
802 0 => sapling_crypto::Rseed::BeforeZip212(
803 jubjub::Fr::from_bytes(&rseed_bytes).expect("should read valid jubjub bytes"),
804 ),
805 1 => sapling_crypto::Rseed::AfterZip212(rseed_bytes),
806 _ => {
807 return Err(std::io::Error::new(
808 std::io::ErrorKind::InvalidData,
809 "invalid rseed zip212 byte",
810 ));
811 }
812 };
813
814 let mut memo_bytes = [0u8; 512];
815 reader.read_exact(&mut memo_bytes)?;
816 let memo = Memo::from_bytes(&memo_bytes).map_err(|e| {
817 std::io::Error::new(
818 std::io::ErrorKind::InvalidData,
819 format!("failed to read memo. {e}"),
820 )
821 })?;
822
823 let recipient_unified_address = Optional::read(&mut reader, |r| {
824 let encoded_address = read_string(r)?;
825
826 decode_unified_address(consensus_parameters, &encoded_address)
827 })?;
828
829 Ok(Self {
830 output_id: OutputId::new(txid, output_index),
831 key_id: KeyId::from_parts(account_id, scope),
832 note: sapling_crypto::Note::from_parts(recipient, value, rseed),
833 memo,
834 recipient_full_unified_address: recipient_unified_address,
835 })
836 }
837
838 pub fn write<W: Write>(
840 &self,
841 mut writer: W,
842 consensus_parameters: &impl consensus::Parameters,
843 ) -> std::io::Result<()> {
844 writer.write_u8(Self::serialized_version())?;
845
846 self.output_id.txid().write(&mut writer)?;
847 writer.write_u16::<LittleEndian>(self.output_id.output_index())?;
848
849 writer.write_u32::<LittleEndian>(self.key_id.account_id.into())?;
850 writer.write_u8(self.key_id.scope as u8)?;
851
852 writer.write_all(&self.note.recipient().to_bytes())?;
853 writer.write_u64::<LittleEndian>(self.value())?;
854 match self.note.rseed() {
855 sapling_crypto::Rseed::BeforeZip212(fr) => {
856 writer.write_u8(0)?;
857 writer.write_all(&fr.to_bytes())?;
858 }
859 sapling_crypto::Rseed::AfterZip212(bytes) => {
860 writer.write_u8(1)?;
861 writer.write_all(bytes)?;
862 }
863 }
864
865 writer.write_all(self.memo.encode().as_array())?;
866 Optional::write(
867 &mut writer,
868 self.recipient_full_unified_address.as_ref(),
869 |w, unified_address| write_string(w, &unified_address.encode(consensus_parameters)),
870 )?;
871
872 Ok(())
873 }
874}
875
876impl OutgoingOrchardNote {
877 pub fn read<R: Read>(
879 mut reader: R,
880 consensus_parameters: &impl consensus::Parameters,
881 ) -> std::io::Result<Self> {
882 let _version = reader.read_u8()?;
883
884 let txid = TxId::read(&mut reader)?;
885 let output_index = reader.read_u16::<LittleEndian>()?;
886
887 let account_id =
888 zip32::AccountId::try_from(reader.read_u32::<LittleEndian>()?).map_err(|e| {
889 std::io::Error::new(
890 std::io::ErrorKind::InvalidData,
891 format!("failed to read account id. {e}"),
892 )
893 })?;
894 let scope = match reader.read_u8()? {
895 0 => Ok(zip32::Scope::External),
896 1 => Ok(zip32::Scope::Internal),
897 _ => Err(std::io::Error::new(
898 std::io::ErrorKind::InvalidData,
899 "invalid scope value",
900 )),
901 }?;
902
903 let mut address_bytes = [0u8; 43];
904 reader.read_exact(&mut address_bytes)?;
905 let recipient = orchard::Address::from_raw_address_bytes(&address_bytes)
906 .expect("should be a valid address");
907 let value = orchard::value::NoteValue::from_raw(reader.read_u64::<LittleEndian>()?);
908 let mut rho_bytes = [0u8; 32];
909 reader.read_exact(&mut rho_bytes)?;
910 let rho = orchard::note::Rho::from_bytes(&rho_bytes).expect("should be valid rho bytes");
911 let mut rseed_bytes = [0u8; 32];
912 reader.read_exact(&mut rseed_bytes)?;
913 let rseed = orchard::note::RandomSeed::from_bytes(rseed_bytes, &rho)
914 .expect("should be valid random seed bytes");
915
916 let mut memo_bytes = [0u8; 512];
917 reader.read_exact(&mut memo_bytes)?;
918 let memo = Memo::from_bytes(&memo_bytes).map_err(|e| {
919 std::io::Error::new(
920 std::io::ErrorKind::InvalidData,
921 format!("failed to read memo. {e}"),
922 )
923 })?;
924
925 let recipient_unified_address = Optional::read(&mut reader, |r| {
926 let encoded_address = read_string(r)?;
927
928 decode_unified_address(consensus_parameters, &encoded_address)
929 })?;
930
931 Ok(Self {
932 output_id: OutputId::new(txid, output_index),
933 key_id: KeyId::from_parts(account_id, scope),
934 note: orchard::note::Note::from_parts(recipient, value, rho, rseed)
935 .expect("should be a valid orchard note"),
936 memo,
937 recipient_full_unified_address: recipient_unified_address,
938 })
939 }
940
941 pub fn write<W: Write>(
943 &self,
944 mut writer: W,
945 consensus_parameters: &impl consensus::Parameters,
946 ) -> std::io::Result<()> {
947 writer.write_u8(Self::serialized_version())?;
948
949 self.output_id.txid().write(&mut writer)?;
950 writer.write_u16::<LittleEndian>(self.output_id.output_index())?;
951
952 writer.write_u32::<LittleEndian>(self.key_id.account_id.into())?;
953 writer.write_u8(self.key_id.scope as u8)?;
954
955 writer.write_all(&self.note.recipient().to_raw_address_bytes())?;
956 writer.write_u64::<LittleEndian>(self.value())?;
957 writer.write_all(&self.note.rho().to_bytes())?;
958 writer.write_all(self.note.rseed().as_bytes())?;
959
960 writer.write_all(self.memo.encode().as_array())?;
961 Optional::write(
962 &mut writer,
963 self.recipient_full_unified_address.as_ref(),
964 |w, unified_address| write_string(w, &unified_address.encode(consensus_parameters)),
965 )?;
966
967 Ok(())
968 }
969}
970
971impl ShardTrees {
972 fn serialized_version() -> u8 {
973 0
974 }
975
976 pub fn read<R: Read>(mut reader: R) -> std::io::Result<Self> {
978 let _version = reader.read_u8()?;
979 let sapling = Self::read_shardtree(&mut reader)?;
980 let orchard = Self::read_shardtree(&mut reader)?;
981
982 Ok(Self { sapling, orchard })
983 }
984
985 pub fn write<W: Write>(&mut self, mut writer: W) -> std::io::Result<()> {
987 writer.write_u8(Self::serialized_version())?;
988 Self::write_shardtree(&mut writer, &mut self.sapling)?;
989 Self::write_shardtree(&mut writer, &mut self.orchard)?;
990
991 Ok(())
992 }
993
994 fn read_shardtree<
995 H: Hashable + Clone + HashSer + Eq,
996 C: Ord + std::fmt::Debug + Copy + From<u32>,
997 R: Read,
998 const DEPTH: u8,
999 const SHARD_HEIGHT: u8,
1000 >(
1001 mut reader: R,
1002 ) -> std::io::Result<ShardTree<MemoryShardStore<H, C>, DEPTH, SHARD_HEIGHT>> {
1003 let shards = Vector::read(&mut reader, |r| {
1004 let level = incrementalmerkletree::Level::from(r.read_u8()?);
1005 let index = r.read_u64::<LittleEndian>()?;
1006 let root_addr = incrementalmerkletree::Address::from_parts(level, index);
1007 let shard = read_shard(r)?;
1008
1009 LocatedPrunableTree::from_parts(root_addr, shard).map_err(|addr| {
1010 std::io::Error::new(
1011 std::io::ErrorKind::InvalidData,
1012 format!("parent node in root has level 0 relative to root address: {addr:?}"),
1013 )
1014 })
1015 })?;
1016 let mut store = MemoryShardStore::empty();
1017 for shard in shards {
1018 store.put_shard(shard).expect("infallible");
1019 }
1020 let checkpoints = Vector::read(&mut reader, |r| {
1021 let checkpoint_id = C::from(r.read_u32::<LittleEndian>()?);
1022 let tree_state = match r.read_u8()? {
1023 0 => TreeState::Empty,
1024 1 => TreeState::AtPosition(Position::from(r.read_u64::<LittleEndian>()?)),
1025 otherwise => {
1026 return Err(std::io::Error::new(
1027 std::io::ErrorKind::InvalidData,
1028 format!(
1029 "failed to read TreeState. expected boolean value, found {otherwise}"
1030 ),
1031 ));
1032 }
1033 };
1034 let marks_removed =
1035 Vector::read(r, |r| r.read_u64::<LittleEndian>().map(Position::from))?;
1036 Ok((
1037 checkpoint_id,
1038 Checkpoint::from_parts(tree_state, marks_removed.into_iter().collect()),
1039 ))
1040 })?;
1041 for (checkpoint_id, checkpoint) in checkpoints {
1042 store
1043 .add_checkpoint(checkpoint_id, checkpoint)
1044 .expect("Infallible");
1045 }
1046 store.put_cap(read_shard(reader)?).expect("Infallible");
1047
1048 Ok(shardtree::ShardTree::new(
1049 store,
1050 MAX_REORG_ALLOWANCE as usize,
1051 ))
1052 }
1053
1054 fn write_shardtree<
1056 H: Hashable + Clone + Eq + HashSer,
1057 C: Ord + std::fmt::Debug + Copy,
1058 W: Write,
1059 const DEPTH: u8,
1060 const SHARD_HEIGHT: u8,
1061 >(
1062 mut writer: W,
1063 shardtree: &mut ShardTree<MemoryShardStore<H, C>, DEPTH, SHARD_HEIGHT>,
1064 ) -> std::io::Result<()>
1065 where
1066 u32: From<C>,
1067 {
1068 fn write_shards<W, H, C>(
1069 mut writer: W,
1070 store: &MemoryShardStore<H, C>,
1071 ) -> std::io::Result<()>
1072 where
1073 H: Hashable + Clone + Eq + HashSer,
1074 C: Ord + std::fmt::Debug + Copy,
1075 W: Write,
1076 {
1077 let roots = store.get_shard_roots().expect("Infallible");
1078 Vector::write(&mut writer, &roots, |w, root| {
1079 w.write_u8(root.level().into())?;
1080 w.write_u64::<LittleEndian>(root.index())?;
1081 let shard = store
1082 .get_shard(*root)
1083 .expect("Infallible")
1084 .expect("cannot find root that shard store claims to have");
1085 write_shard(w, shard.root())
1086 })
1087 }
1088
1089 fn write_checkpoints<W, Cid>(
1090 mut writer: W,
1091 checkpoints: &[(Cid, Checkpoint)],
1092 ) -> std::io::Result<()>
1093 where
1094 W: Write,
1095 Cid: Ord + std::fmt::Debug + Copy,
1096 u32: From<Cid>,
1097 {
1098 Vector::write(
1099 &mut writer,
1100 checkpoints,
1101 |mut w, (checkpoint_id, checkpoint)| {
1102 w.write_u32::<LittleEndian>(u32::from(*checkpoint_id))?;
1103 match checkpoint.tree_state() {
1104 shardtree::store::TreeState::Empty => w.write_u8(0),
1105 shardtree::store::TreeState::AtPosition(pos) => {
1106 w.write_u8(1)?;
1107 w.write_u64::<LittleEndian>(<u64 as From<Position>>::from(pos))
1108 }
1109 }?;
1110 Vector::write(
1111 &mut w,
1112 &checkpoint.marks_removed().iter().collect::<Vec<_>>(),
1113 |w, mark| {
1114 w.write_u64::<LittleEndian>(<u64 as From<Position>>::from(**mark))
1115 },
1116 )
1117 },
1118 )
1119 }
1120
1121 let mut store = std::mem::replace(
1123 shardtree,
1124 shardtree::ShardTree::new(MemoryShardStore::empty(), 0),
1125 )
1126 .into_store();
1127
1128 macro_rules! write_with_error_handling {
1129 ($writer: ident, $from: ident) => {
1130 if let Err(e) = $writer(&mut writer, &$from) {
1131 *shardtree = shardtree::ShardTree::new(store, MAX_REORG_ALLOWANCE as usize);
1132 return Err(e);
1133 }
1134 };
1135 }
1136
1137 write_with_error_handling!(write_shards, store);
1139
1140 let mut checkpoints = Vec::new();
1142 store
1143 .with_checkpoints(MAX_REORG_ALLOWANCE as usize, |checkpoint_id, checkpoint| {
1144 checkpoints.push((*checkpoint_id, checkpoint.clone()));
1145 Ok(())
1146 })
1147 .expect("Infallible");
1148 write_with_error_handling!(write_checkpoints, checkpoints);
1149
1150 let cap = store.get_cap().expect("Infallible");
1152 write_with_error_handling!(write_shard, cap);
1153
1154 *shardtree = shardtree::ShardTree::new(store, MAX_REORG_ALLOWANCE as usize);
1155
1156 Ok(())
1157 }
1158}