zebra_state/service/finalized_state/disk_format/
chain.rs

1//! Chain data serialization formats for finalized data.
2//!
3//! # Correctness
4//!
5//! [`crate::constants::state_database_format_version_in_code()`] must be incremented
6//! each time the database format (column, serialization, etc) changes.
7
8use std::collections::BTreeMap;
9
10use bincode::Options;
11
12use zebra_chain::{
13    amount::NonNegative,
14    block::Height,
15    block_info::BlockInfo,
16    history_tree::{HistoryTreeError, NonEmptyHistoryTree},
17    parameters::{Network, NetworkKind},
18    primitives::zcash_history,
19    value_balance::ValueBalance,
20};
21
22use crate::service::finalized_state::disk_format::{FromDisk, IntoDisk};
23
24impl IntoDisk for ValueBalance<NonNegative> {
25    type Bytes = [u8; 40];
26
27    fn as_bytes(&self) -> Self::Bytes {
28        self.to_bytes()
29    }
30}
31
32impl FromDisk for ValueBalance<NonNegative> {
33    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
34        ValueBalance::from_bytes(bytes.as_ref()).expect("ValueBalance should be parsable")
35    }
36}
37
38// The following implementations for history trees use `serde` and
39// `bincode`. `serde` serializations depend on the inner structure of the type.
40// They should not be used in new code. (This is an issue for any derived serialization format.)
41//
42// We explicitly use `bincode::DefaultOptions`  to disallow trailing bytes; see
43// https://docs.rs/bincode/1.3.3/bincode/config/index.html#options-struct-vs-bincode-functions
44
45#[derive(serde::Serialize, serde::Deserialize)]
46pub struct HistoryTreeParts {
47    network_kind: NetworkKind,
48    size: u32,
49    peaks: BTreeMap<u32, zcash_history::Entry>,
50    current_height: Height,
51}
52
53impl HistoryTreeParts {
54    /// Converts [`HistoryTreeParts`] to a [`NonEmptyHistoryTree`].
55    pub(crate) fn with_network(
56        self,
57        network: &Network,
58    ) -> Result<NonEmptyHistoryTree, HistoryTreeError> {
59        assert_eq!(
60            self.network_kind,
61            network.kind(),
62            "history tree network kind should match current network"
63        );
64
65        NonEmptyHistoryTree::from_cache(network, self.size, self.peaks, self.current_height)
66    }
67}
68
69impl From<&NonEmptyHistoryTree> for HistoryTreeParts {
70    fn from(history_tree: &NonEmptyHistoryTree) -> Self {
71        HistoryTreeParts {
72            network_kind: history_tree.network().kind(),
73            size: history_tree.size(),
74            peaks: history_tree.peaks().clone(),
75            current_height: history_tree.current_height(),
76        }
77    }
78}
79
80impl IntoDisk for HistoryTreeParts {
81    type Bytes = Vec<u8>;
82
83    fn as_bytes(&self) -> Self::Bytes {
84        bincode::DefaultOptions::new()
85            .serialize(self)
86            .expect("serialization to vec doesn't fail")
87    }
88}
89
90impl FromDisk for HistoryTreeParts {
91    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
92        bincode::DefaultOptions::new()
93            .deserialize(bytes.as_ref())
94            .expect("deserialization format should match the serialization format used by IntoDisk")
95    }
96}
97
98impl IntoDisk for BlockInfo {
99    type Bytes = Vec<u8>;
100
101    fn as_bytes(&self) -> Self::Bytes {
102        self.value_pools()
103            .as_bytes()
104            .iter()
105            .copied()
106            .chain(self.size().to_le_bytes().iter().copied())
107            .collect()
108    }
109}
110
111impl FromDisk for BlockInfo {
112    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
113        // We want to be forward-compatible, so this must work even if the
114        // size of the buffer is larger than expected.
115        match bytes.as_ref().len() {
116            44.. => {
117                let value_pools = ValueBalance::<NonNegative>::from_bytes(&bytes.as_ref()[0..40])
118                    .expect("must work for 40 bytes");
119                let size =
120                    u32::from_le_bytes(bytes.as_ref()[40..44].try_into().expect("must be 4 bytes"));
121                BlockInfo::new(value_pools, size)
122            }
123            _ => panic!("invalid format"),
124        }
125    }
126}