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