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