zebra_state/service/finalized_state/disk_format/
chain.rs1use std::collections::BTreeMap;
9
10use bincode::Options;
11use serde_big_array::BigArray;
12
13use zebra_chain::{
14 amount::{Amount, NonNegative},
15 block::Height,
16 block_info::BlockInfo,
17 history_tree::{HistoryTreeError, NonEmptyHistoryTree},
18 parameters::{Network, NetworkKind},
19 primitives::zcash_history,
20 value_balance::{ValueBalance, SERIALIZED_SIZE},
21};
22
23use crate::service::finalized_state::disk_format::{FromDisk, IntoDisk};
24
25impl IntoDisk for ValueBalance<NonNegative> {
26 type Bytes = [u8; SERIALIZED_SIZE];
27
28 fn as_bytes(&self) -> Self::Bytes {
29 self.to_bytes()
30 }
31}
32
33impl FromDisk for ValueBalance<NonNegative> {
34 fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
35 ValueBalance::from_bytes(bytes.as_ref()).expect("ValueBalance should be parsable")
36 }
37}
38
39#[derive(serde::Serialize, serde::Deserialize)]
47pub struct HistoryTreeParts {
48 network_kind: NetworkKind,
49 size: u32,
50 peaks: BTreeMap<u32, zcash_history::Entry>,
51 current_height: Height,
52}
53
54impl HistoryTreeParts {
55 pub(crate) fn with_network(
57 self,
58 network: &Network,
59 ) -> Result<NonEmptyHistoryTree, HistoryTreeError> {
60 assert_eq!(
61 self.network_kind,
62 network.kind(),
63 "history tree network kind should match current network"
64 );
65
66 NonEmptyHistoryTree::from_cache(network, self.size, self.peaks, self.current_height)
67 }
68}
69
70impl From<&NonEmptyHistoryTree> for HistoryTreeParts {
71 fn from(history_tree: &NonEmptyHistoryTree) -> Self {
72 HistoryTreeParts {
73 network_kind: history_tree.network().kind(),
74 size: history_tree.size(),
75 peaks: history_tree.peaks().clone(),
76 current_height: history_tree.current_height(),
77 }
78 }
79}
80
81impl IntoDisk for HistoryTreeParts {
82 type Bytes = Vec<u8>;
83
84 fn as_bytes(&self) -> Self::Bytes {
85 bincode::DefaultOptions::new()
86 .serialize(self)
87 .expect("serialization to vec doesn't fail")
88 }
89}
90
91const LEGACY_MAX_ENTRY_SIZE: usize = 253;
95
96#[derive(serde::Serialize, serde::Deserialize)]
102struct LegacyHistoryTreeParts {
103 network_kind: NetworkKind,
104 size: u32,
105 peaks: BTreeMap<u32, LegacyEntry>,
106 current_height: Height,
107}
108
109#[derive(serde::Serialize, serde::Deserialize)]
111struct LegacyEntry {
112 #[serde(with = "BigArray")]
113 inner: [u8; LEGACY_MAX_ENTRY_SIZE],
114}
115
116impl From<LegacyHistoryTreeParts> for HistoryTreeParts {
117 fn from(legacy: LegacyHistoryTreeParts) -> Self {
118 HistoryTreeParts {
119 network_kind: legacy.network_kind,
120 size: legacy.size,
121 peaks: legacy
122 .peaks
123 .into_iter()
124 .map(|(index, entry)| {
125 (
126 index,
127 zcash_history::Entry::from_raw_bytes_padded(&entry.inner),
128 )
129 })
130 .collect(),
131 current_height: legacy.current_height,
132 }
133 }
134}
135
136impl FromDisk for HistoryTreeParts {
137 fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
138 let bytes = bytes.as_ref();
139 let options = bincode::DefaultOptions::new();
140
141 options
154 .deserialize::<HistoryTreeParts>(bytes)
155 .or_else(|_| {
156 options
157 .deserialize::<LegacyHistoryTreeParts>(bytes)
158 .map(HistoryTreeParts::from)
159 })
160 .expect("deserialization format should match the serialization format used by IntoDisk")
161 }
162}
163
164impl IntoDisk for BlockInfo {
165 type Bytes = [u8; SERIALIZED_SIZE + 4];
166
167 fn as_bytes(&self) -> Self::Bytes {
168 let mut bytes = [0; SERIALIZED_SIZE + 4];
169 let value_pools = self.value_pools().to_bytes();
170 bytes[..48].copy_from_slice(&value_pools[..48]);
171 bytes[48..52].copy_from_slice(&self.size().to_le_bytes());
172 bytes[52..].copy_from_slice(&value_pools[48..]);
173 bytes
174 }
175}
176
177impl FromDisk for BlockInfo {
178 fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
179 let bytes = bytes.as_ref();
180
181 match bytes.len() {
184 60.. => {
185 let mut value_pools = ValueBalance::<NonNegative>::from_bytes(&bytes[..48])
186 .expect("must work for 48 bytes");
187 let size = u32::from_le_bytes(bytes[48..52].try_into().expect("must be 4 bytes"));
188 let nsm = Amount::from_bytes(bytes[52..60].try_into().expect("must be 8 bytes"))
189 .expect("stored NSM balance must be a valid nonnegative amount");
190 value_pools.set_nsm_amount(nsm);
191 BlockInfo::new(value_pools, size)
192 }
193 52.. => {
196 let value_pools = ValueBalance::<NonNegative>::from_bytes(&bytes[0..48])
197 .expect("must work for 48 bytes");
198 let size = u32::from_le_bytes(bytes[48..52].try_into().expect("must be 4 bytes"));
199 BlockInfo::new(value_pools, size)
200 }
201 44.. => {
204 let value_pools = ValueBalance::<NonNegative>::from_bytes(&bytes[0..40])
205 .expect("must work for 40 bytes");
206 let size = u32::from_le_bytes(bytes[40..44].try_into().expect("must be 4 bytes"));
207 BlockInfo::new(value_pools, size)
208 }
209 _ => panic!("invalid format"),
210 }
211 }
212}
213
214#[cfg(test)]
215mod tests;