1use std::{cmp::max, fmt::Debug};
9
10use zebra_chain::{
11 amount::{self, Amount, Constraint, NegativeAllowed, NonNegative},
12 block::Height,
13 parameters::NetworkKind,
14 serialization::{ZcashDeserializeInto, ZcashSerialize},
15 transparent::{self, Address::*, OutputIndex},
16};
17
18use crate::service::finalized_state::disk_format::{
19 block::{TransactionIndex, TransactionLocation, TRANSACTION_LOCATION_DISK_BYTES},
20 expand_zero_be_bytes, truncate_zero_be_bytes, FromDisk, IntoDisk,
21};
22
23#[cfg(any(test, feature = "proptest-impl"))]
24use proptest_derive::Arbitrary;
25
26pub const BALANCE_DISK_BYTES: usize = 8;
28
29pub const OUTPUT_INDEX_DISK_BYTES: usize = 3;
33
34pub const MAX_ON_DISK_OUTPUT_INDEX: OutputIndex =
46 OutputIndex::from_index((1 << (OUTPUT_INDEX_DISK_BYTES * 8)) - 1);
47
48pub const OUTPUT_LOCATION_DISK_BYTES: usize =
53 TRANSACTION_LOCATION_DISK_BYTES + OUTPUT_INDEX_DISK_BYTES;
54
55#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
62#[cfg_attr(
63 any(test, feature = "proptest-impl"),
64 derive(Arbitrary, serde::Serialize, serde::Deserialize)
65)]
66pub struct OutputLocation {
67 transaction_location: TransactionLocation,
69
70 output_index: OutputIndex,
72}
73
74impl OutputLocation {
75 #[allow(dead_code)]
77 pub fn from_usize(
78 height: Height,
79 transaction_index: usize,
80 output_index: usize,
81 ) -> OutputLocation {
82 OutputLocation {
83 transaction_location: TransactionLocation::from_usize(height, transaction_index),
84 output_index: OutputIndex::from_usize(output_index),
85 }
86 }
87
88 pub fn from_outpoint(
94 transaction_location: TransactionLocation,
95 outpoint: &transparent::OutPoint,
96 ) -> OutputLocation {
97 OutputLocation::from_output_index(transaction_location, outpoint.index)
98 }
99
100 pub fn from_output_index(
104 transaction_location: TransactionLocation,
105 output_index: u32,
106 ) -> OutputLocation {
107 OutputLocation {
108 transaction_location,
109 output_index: OutputIndex::from_index(output_index),
110 }
111 }
112
113 pub fn height(&self) -> Height {
115 self.transaction_location.height
116 }
117
118 pub fn transaction_index(&self) -> TransactionIndex {
120 self.transaction_location.index
121 }
122
123 pub fn output_index(&self) -> OutputIndex {
125 self.output_index
126 }
127
128 pub fn transaction_location(&self) -> TransactionLocation {
130 self.transaction_location
131 }
132
133 #[cfg(any(test, feature = "proptest-impl"))]
135 #[allow(dead_code)]
136 pub fn height_mut(&mut self) -> &mut Height {
137 &mut self.transaction_location.height
138 }
139}
140
141pub type AddressLocation = OutputLocation;
152
153#[derive(Copy, Clone, Debug, Eq, PartialEq)]
155#[cfg_attr(
156 any(test, feature = "proptest-impl"),
157 derive(Arbitrary, serde::Serialize, serde::Deserialize),
158 serde(bound = "C: Constraint + Clone")
159)]
160pub struct AddressBalanceLocationInner<C: Constraint + Copy + std::fmt::Debug> {
161 balance: Amount<C>,
163
164 received: u64,
166
167 location: AddressLocation,
169}
170
171impl<C: Constraint + Copy + std::fmt::Debug> AddressBalanceLocationInner<C> {
172 fn new(first_output: OutputLocation) -> Self {
177 Self {
178 balance: Amount::zero(),
179 received: 0,
180 location: first_output,
181 }
182 }
183
184 pub fn balance(&self) -> Amount<C> {
186 self.balance
187 }
188
189 pub fn received(&self) -> u64 {
191 self.received
192 }
193
194 pub fn balance_mut(&mut self) -> &mut Amount<C> {
196 &mut self.balance
197 }
198
199 pub fn received_mut(&mut self) -> &mut u64 {
201 &mut self.received
202 }
203
204 pub fn address_location(&self) -> AddressLocation {
206 self.location
207 }
208
209 #[cfg(any(test, feature = "proptest-impl"))]
211 #[allow(dead_code)]
212 pub fn height_mut(&mut self) -> &mut Height {
213 &mut self.location.transaction_location.height
214 }
215}
216
217impl<C: Constraint + Copy + std::fmt::Debug> std::ops::Add for AddressBalanceLocationInner<C> {
218 type Output = Result<Self, amount::Error>;
219
220 fn add(self, rhs: Self) -> Self::Output {
221 Ok(AddressBalanceLocationInner {
222 balance: (self.balance + rhs.balance)?,
223 received: self.received.saturating_add(rhs.received),
224 location: self.location.min(rhs.location),
237 })
238 }
239}
240
241pub struct AddressBalanceLocationChange(AddressBalanceLocationInner<NegativeAllowed>);
244
245impl AddressBalanceLocationChange {
246 pub fn new(location: AddressLocation) -> Self {
250 Self(AddressBalanceLocationInner::new(location))
251 }
252
253 #[allow(clippy::unwrap_in_result)]
255 pub fn receive_output(
256 &mut self,
257 unspent_output: &transparent::Output,
258 ) -> Result<(), amount::Error> {
259 self.balance = (self
260 .balance
261 .zatoshis()
262 .checked_add(unspent_output.value().zatoshis()))
263 .expect("adding two Amounts is always within an i64")
264 .try_into()?;
265 self.received = self.received.saturating_add(unspent_output.value().into());
266 Ok(())
267 }
268
269 #[allow(clippy::unwrap_in_result)]
271 pub fn spend_output(
272 &mut self,
273 spent_output: &transparent::Output,
274 ) -> Result<(), amount::Error> {
275 self.balance = (self
276 .balance
277 .zatoshis()
278 .checked_sub(spent_output.value().zatoshis()))
279 .expect("subtracting two Amounts is always within an i64")
280 .try_into()?;
281
282 Ok(())
283 }
284}
285
286impl std::ops::Deref for AddressBalanceLocationChange {
287 type Target = AddressBalanceLocationInner<NegativeAllowed>;
288
289 fn deref(&self) -> &Self::Target {
290 &self.0
291 }
292}
293
294impl std::ops::DerefMut for AddressBalanceLocationChange {
295 fn deref_mut(&mut self) -> &mut Self::Target {
296 &mut self.0
297 }
298}
299
300impl std::ops::Add for AddressBalanceLocationChange {
301 type Output = Result<Self, amount::Error>;
302
303 fn add(self, rhs: Self) -> Self::Output {
304 (self.0 + rhs.0).map(Self)
305 }
306}
307
308#[derive(Copy, Clone, Debug, Eq, PartialEq)]
318#[cfg_attr(
319 any(test, feature = "proptest-impl"),
320 derive(Arbitrary, serde::Serialize, serde::Deserialize)
321)]
322pub struct AddressBalanceLocation(AddressBalanceLocationInner<NonNegative>);
323
324impl AddressBalanceLocation {
325 pub fn new(first_output: OutputLocation) -> Self {
329 Self(AddressBalanceLocationInner::new(first_output))
330 }
331
332 pub fn into_new_change(self) -> AddressBalanceLocationChange {
335 AddressBalanceLocationChange::new(self.location)
336 }
337}
338
339impl std::ops::Deref for AddressBalanceLocation {
340 type Target = AddressBalanceLocationInner<NonNegative>;
341
342 fn deref(&self) -> &Self::Target {
343 &self.0
344 }
345}
346
347impl std::ops::DerefMut for AddressBalanceLocation {
348 fn deref_mut(&mut self) -> &mut Self::Target {
349 &mut self.0
350 }
351}
352
353impl std::ops::Add for AddressBalanceLocation {
354 type Output = Result<Self, amount::Error>;
355
356 fn add(self, rhs: Self) -> Self::Output {
357 (self.0 + rhs.0).map(Self)
358 }
359}
360
361#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
370#[cfg_attr(
371 any(test, feature = "proptest-impl"),
372 derive(Arbitrary, serde::Serialize, serde::Deserialize)
373)]
374pub struct AddressUnspentOutput {
375 address_location: AddressLocation,
377
378 unspent_output_location: OutputLocation,
380}
381
382impl AddressUnspentOutput {
383 pub fn new(
386 address_location: AddressLocation,
387 unspent_output_location: OutputLocation,
388 ) -> AddressUnspentOutput {
389 AddressUnspentOutput {
390 address_location,
391 unspent_output_location,
392 }
393 }
394
395 pub fn address_iterator_start(address_location: AddressLocation) -> AddressUnspentOutput {
406 let zero_output_location = OutputLocation::from_usize(Height(0), 0, 0);
408
409 AddressUnspentOutput {
410 address_location,
411 unspent_output_location: zero_output_location,
412 }
413 }
414
415 pub fn address_iterator_next(&mut self) {
425 self.unspent_output_location.output_index += 1;
430 }
431
432 pub fn address_location(&self) -> AddressLocation {
436 self.address_location
437 }
438
439 pub fn unspent_output_location(&self) -> OutputLocation {
441 self.unspent_output_location
442 }
443
444 #[cfg(any(test, feature = "proptest-impl"))]
446 #[allow(dead_code)]
447 pub fn address_location_mut(&mut self) -> &mut AddressLocation {
448 &mut self.address_location
449 }
450
451 #[cfg(any(test, feature = "proptest-impl"))]
453 #[allow(dead_code)]
454 pub fn unspent_output_location_mut(&mut self) -> &mut OutputLocation {
455 &mut self.unspent_output_location
456 }
457}
458
459#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
468#[cfg_attr(
469 any(test, feature = "proptest-impl"),
470 derive(Arbitrary, serde::Serialize, serde::Deserialize)
471)]
472pub struct AddressTransaction {
473 address_location: AddressLocation,
475
476 transaction_location: TransactionLocation,
478}
479
480impl AddressTransaction {
481 pub fn new(
484 address_location: AddressLocation,
485 transaction_location: TransactionLocation,
486 ) -> AddressTransaction {
487 AddressTransaction {
488 address_location,
489 transaction_location,
490 }
491 }
492
493 pub fn address_iterator_range(
508 address_location: AddressLocation,
509 query: std::ops::RangeInclusive<Height>,
510 ) -> std::ops::RangeInclusive<AddressTransaction> {
511 let first_utxo_location = address_location.transaction_location();
516
517 let query_start_location = TransactionLocation::from_index(*query.start(), 0);
519 let query_end_location = TransactionLocation::from_index(*query.end(), u16::MAX);
520
521 let addr_tx = |tx_loc| AddressTransaction::new(address_location, tx_loc);
522
523 addr_tx(max(first_utxo_location, query_start_location))..=addr_tx(query_end_location)
524 }
525
526 #[allow(dead_code)]
536 pub fn address_iterator_next(&mut self) {
537 self.transaction_location.index.0 += 1;
542 }
543
544 pub fn address_location(&self) -> AddressLocation {
548 self.address_location
549 }
550
551 pub fn transaction_location(&self) -> TransactionLocation {
553 self.transaction_location
554 }
555
556 #[cfg(any(test, feature = "proptest-impl"))]
558 #[allow(dead_code)]
559 pub fn address_location_mut(&mut self) -> &mut AddressLocation {
560 &mut self.address_location
561 }
562
563 #[cfg(any(test, feature = "proptest-impl"))]
565 #[allow(dead_code)]
566 pub fn transaction_location_mut(&mut self) -> &mut TransactionLocation {
567 &mut self.transaction_location
568 }
569}
570
571fn address_variant(address: &transparent::Address) -> u8 {
575 use NetworkKind::*;
576 match (address.network_kind(), address) {
580 (Mainnet, PayToPublicKeyHash { .. }) => 0,
581 (Mainnet, PayToScriptHash { .. }) => 1,
582 (Testnet | Regtest, PayToPublicKeyHash { .. }) => 2,
586 (Testnet | Regtest, PayToScriptHash { .. }) => 3,
587 (Mainnet, Tex { .. }) => 4,
589 (Testnet | Regtest, Tex { .. }) => 5,
590 }
591}
592
593impl IntoDisk for transparent::Address {
594 type Bytes = [u8; 21];
595
596 fn as_bytes(&self) -> Self::Bytes {
597 let variant_bytes = vec![address_variant(self)];
598 let hash_bytes = self.hash_bytes().to_vec();
599
600 [variant_bytes, hash_bytes].concat().try_into().unwrap()
601 }
602}
603
604#[cfg(any(test, feature = "proptest-impl"))]
605impl FromDisk for transparent::Address {
606 fn from_bytes(disk_bytes: impl AsRef<[u8]>) -> Self {
607 let (address_variant, hash_bytes) = disk_bytes.as_ref().split_at(1);
608
609 let address_variant = address_variant[0];
610 let hash_bytes = hash_bytes.try_into().unwrap();
611
612 let network = if address_variant < 2 {
613 NetworkKind::Mainnet
614 } else {
615 NetworkKind::Testnet
616 };
617
618 if address_variant % 2 == 0 {
619 transparent::Address::from_pub_key_hash(network, hash_bytes)
620 } else {
621 transparent::Address::from_script_hash(network, hash_bytes)
622 }
623 }
624}
625
626impl<C: Constraint> IntoDisk for Amount<C> {
627 type Bytes = [u8; BALANCE_DISK_BYTES];
628
629 fn as_bytes(&self) -> Self::Bytes {
630 self.to_bytes()
631 }
632}
633
634impl FromDisk for Amount<NonNegative> {
635 fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
636 let array = bytes.as_ref().try_into().unwrap();
637 Amount::from_bytes(array).unwrap()
638 }
639}
640
641impl IntoDisk for OutputIndex {
642 type Bytes = [u8; OUTPUT_INDEX_DISK_BYTES];
643
644 fn as_bytes(&self) -> Self::Bytes {
645 let mem_bytes = self.index().to_be_bytes();
646
647 let disk_bytes = truncate_zero_be_bytes(&mem_bytes, OUTPUT_INDEX_DISK_BYTES);
648
649 match disk_bytes {
650 Some(b) => b.try_into().unwrap(),
651 None => {
661 #[cfg(test)]
662 {
663 use zebra_chain::serialization::TrustedPreallocate;
664 assert!(
665 u64::from(MAX_ON_DISK_OUTPUT_INDEX.index())
666 > zebra_chain::transparent::Output::max_allocation(),
667 "increased block size requires database output index format change",
668 );
669 }
670
671 truncate_zero_be_bytes(
672 &MAX_ON_DISK_OUTPUT_INDEX.index().to_be_bytes(),
673 OUTPUT_INDEX_DISK_BYTES,
674 )
675 .expect("max on disk output index is valid")
676 .try_into()
677 .unwrap()
678 }
679 }
680 }
681}
682
683impl FromDisk for OutputIndex {
684 fn from_bytes(disk_bytes: impl AsRef<[u8]>) -> Self {
685 let mem_len = u32::BITS / 8;
686 let mem_len = mem_len.try_into().unwrap();
687
688 let mem_bytes = expand_zero_be_bytes(disk_bytes.as_ref(), mem_len);
689 let mem_bytes = mem_bytes.try_into().unwrap();
690 OutputIndex::from_index(u32::from_be_bytes(mem_bytes))
691 }
692}
693
694impl IntoDisk for OutputLocation {
695 type Bytes = [u8; OUTPUT_LOCATION_DISK_BYTES];
696
697 fn as_bytes(&self) -> Self::Bytes {
698 let transaction_location_bytes = self.transaction_location().as_bytes().to_vec();
699 let output_index_bytes = self.output_index().as_bytes().to_vec();
700
701 [transaction_location_bytes, output_index_bytes]
702 .concat()
703 .try_into()
704 .unwrap()
705 }
706}
707
708impl FromDisk for OutputLocation {
709 fn from_bytes(disk_bytes: impl AsRef<[u8]>) -> Self {
710 let (transaction_location_bytes, output_index_bytes) = disk_bytes
711 .as_ref()
712 .split_at(TRANSACTION_LOCATION_DISK_BYTES);
713
714 let transaction_location = TransactionLocation::from_bytes(transaction_location_bytes);
715 let output_index = OutputIndex::from_bytes(output_index_bytes);
716
717 OutputLocation {
718 transaction_location,
719 output_index,
720 }
721 }
722}
723
724impl<C: Constraint + Copy + std::fmt::Debug> IntoDisk for AddressBalanceLocationInner<C> {
725 type Bytes = [u8; BALANCE_DISK_BYTES + OUTPUT_LOCATION_DISK_BYTES + size_of::<u64>()];
726
727 fn as_bytes(&self) -> Self::Bytes {
728 let balance_bytes = self.balance().as_bytes().to_vec();
729 let address_location_bytes = self.address_location().as_bytes().to_vec();
730 let received_bytes = self.received().to_le_bytes().to_vec();
731
732 [balance_bytes, address_location_bytes, received_bytes]
733 .concat()
734 .try_into()
735 .unwrap()
736 }
737}
738
739impl IntoDisk for AddressBalanceLocation {
740 type Bytes = [u8; BALANCE_DISK_BYTES + OUTPUT_LOCATION_DISK_BYTES + size_of::<u64>()];
741
742 fn as_bytes(&self) -> Self::Bytes {
743 self.0.as_bytes()
744 }
745}
746
747impl IntoDisk for AddressBalanceLocationChange {
748 type Bytes = [u8; BALANCE_DISK_BYTES + OUTPUT_LOCATION_DISK_BYTES + size_of::<u64>()];
749
750 fn as_bytes(&self) -> Self::Bytes {
751 self.0.as_bytes()
752 }
753}
754
755impl<C: Constraint + Copy + std::fmt::Debug> FromDisk for AddressBalanceLocationInner<C> {
756 fn from_bytes(disk_bytes: impl AsRef<[u8]>) -> Self {
757 let (balance_bytes, rest) = disk_bytes.as_ref().split_at(BALANCE_DISK_BYTES);
758 let (address_location_bytes, rest) = rest.split_at(BALANCE_DISK_BYTES);
759 let (received_bytes, _) = rest.split_at_checked(size_of::<u64>()).unwrap_or_default();
760
761 let balance = Amount::from_bytes(balance_bytes.try_into().unwrap()).unwrap();
762 let address_location = AddressLocation::from_bytes(address_location_bytes);
763 let received = u64::from_le_bytes(received_bytes.try_into().unwrap_or_default());
767
768 let mut address_balance_location = Self::new(address_location);
769 *address_balance_location.balance_mut() = balance;
770 *address_balance_location.received_mut() = received;
771
772 address_balance_location
773 }
774}
775
776impl FromDisk for AddressBalanceLocation {
777 fn from_bytes(disk_bytes: impl AsRef<[u8]>) -> Self {
778 Self(AddressBalanceLocationInner::from_bytes(disk_bytes))
779 }
780}
781
782impl FromDisk for AddressBalanceLocationChange {
783 fn from_bytes(disk_bytes: impl AsRef<[u8]>) -> Self {
784 Self(AddressBalanceLocationInner::from_bytes(disk_bytes))
785 }
786}
787
788impl IntoDisk for transparent::Output {
789 type Bytes = Vec<u8>;
790
791 fn as_bytes(&self) -> Self::Bytes {
792 self.zcash_serialize_to_vec().unwrap()
793 }
794}
795
796impl FromDisk for transparent::Output {
797 fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
798 bytes.as_ref().zcash_deserialize_into().unwrap()
799 }
800}
801
802impl IntoDisk for AddressUnspentOutput {
803 type Bytes = [u8; OUTPUT_LOCATION_DISK_BYTES + OUTPUT_LOCATION_DISK_BYTES];
804
805 fn as_bytes(&self) -> Self::Bytes {
806 let address_location_bytes = self.address_location().as_bytes();
807 let unspent_output_location_bytes = self.unspent_output_location().as_bytes();
808
809 [address_location_bytes, unspent_output_location_bytes]
810 .concat()
811 .try_into()
812 .unwrap()
813 }
814}
815
816impl FromDisk for AddressUnspentOutput {
817 fn from_bytes(disk_bytes: impl AsRef<[u8]>) -> Self {
818 let (address_location_bytes, unspent_output_location_bytes) =
819 disk_bytes.as_ref().split_at(OUTPUT_LOCATION_DISK_BYTES);
820
821 let address_location = AddressLocation::from_bytes(address_location_bytes);
822 let unspent_output_location = AddressLocation::from_bytes(unspent_output_location_bytes);
823
824 AddressUnspentOutput::new(address_location, unspent_output_location)
825 }
826}
827
828impl IntoDisk for AddressTransaction {
829 type Bytes = [u8; OUTPUT_LOCATION_DISK_BYTES + TRANSACTION_LOCATION_DISK_BYTES];
830
831 fn as_bytes(&self) -> Self::Bytes {
832 let address_location_bytes: [u8; OUTPUT_LOCATION_DISK_BYTES] =
833 self.address_location().as_bytes();
834 let transaction_location_bytes: [u8; TRANSACTION_LOCATION_DISK_BYTES] =
835 self.transaction_location().as_bytes();
836
837 address_location_bytes
838 .iter()
839 .copied()
840 .chain(transaction_location_bytes.iter().copied())
841 .collect::<Vec<u8>>()
842 .try_into()
843 .expect("concatenation of fixed-sized arrays should have the correct size")
844 }
845}
846
847impl FromDisk for AddressTransaction {
848 fn from_bytes(disk_bytes: impl AsRef<[u8]>) -> Self {
849 let (address_location_bytes, transaction_location_bytes) =
850 disk_bytes.as_ref().split_at(OUTPUT_LOCATION_DISK_BYTES);
851
852 let address_location = AddressLocation::from_bytes(address_location_bytes);
853 let transaction_location = TransactionLocation::from_bytes(transaction_location_bytes);
854
855 AddressTransaction::new(address_location, transaction_location)
856 }
857}