zebra_state/service/finalized_state/
disk_format.rs

1//! 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::sync::Arc;
9
10pub mod block;
11pub mod chain;
12pub mod shielded;
13pub mod transparent;
14pub mod upgrade;
15
16#[cfg(feature = "shielded-scan")]
17pub mod scan;
18
19#[cfg(any(test, feature = "proptest-impl"))]
20mod tests;
21
22pub use block::{TransactionIndex, TransactionLocation, MAX_ON_DISK_HEIGHT};
23pub use transparent::{OutputIndex, OutputLocation};
24
25#[cfg(feature = "shielded-scan")]
26pub use scan::{
27    SaplingScannedDatabaseEntry, SaplingScannedDatabaseIndex, SaplingScannedResult,
28    SaplingScanningKey,
29};
30
31#[cfg(any(test, feature = "proptest-impl"))]
32pub use tests::KV;
33
34/// Helper type for writing types to disk as raw bytes.
35/// Also used to convert key types to raw bytes for disk lookups.
36pub trait IntoDisk {
37    /// The type used to write bytes to disk,
38    /// and compare a value as a key to on-disk keys.
39    type Bytes: AsRef<[u8]>;
40
41    /// Converts the current type into serialized raw bytes.
42    ///
43    /// Used to convert keys to bytes in [`ReadDisk`][1],
44    /// and keys and values to bytes in [`WriteDisk`][2].
45    ///
46    /// # Panics
47    ///
48    /// - if the input data doesn't serialize correctly
49    ///
50    /// [1]: super::disk_db::ReadDisk
51    /// [2]: super::disk_db::WriteDisk
52    fn as_bytes(&self) -> Self::Bytes;
53}
54
55/// Helper type for reading types from disk as raw bytes.
56pub trait FromDisk: Sized {
57    /// Converts raw disk bytes back into the deserialized type.
58    ///
59    /// Used to convert keys and values from bytes in [`ReadDisk`][1].
60    ///
61    /// # Panics
62    ///
63    /// - if the input data doesn't deserialize correctly
64    ///
65    /// [1]: super::disk_db::ReadDisk
66    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self;
67}
68
69// Generic serialization impls
70
71impl<T> IntoDisk for &T
72where
73    T: IntoDisk,
74{
75    type Bytes = T::Bytes;
76
77    fn as_bytes(&self) -> Self::Bytes {
78        T::as_bytes(*self)
79    }
80}
81
82impl<T> IntoDisk for Arc<T>
83where
84    T: IntoDisk,
85{
86    type Bytes = T::Bytes;
87
88    fn as_bytes(&self) -> Self::Bytes {
89        T::as_bytes(self)
90    }
91}
92
93impl<T> FromDisk for Arc<T>
94where
95    T: FromDisk,
96{
97    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
98        Arc::new(T::from_bytes(bytes))
99    }
100}
101
102// Commonly used serialization impls
103
104impl IntoDisk for () {
105    type Bytes = [u8; 0];
106
107    fn as_bytes(&self) -> Self::Bytes {
108        []
109    }
110}
111
112impl FromDisk for () {
113    #[allow(clippy::unused_unit)]
114    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
115        assert_eq!(
116            bytes.as_ref().len(),
117            0,
118            "unexpected data in zero-sized column family type",
119        );
120
121        ()
122    }
123}
124
125/// Access database keys or values as raw bytes.
126/// Mainly for use in tests, runtime checks, or format compatibility code.
127#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
128pub struct RawBytes(Vec<u8>);
129
130// Note: don't implement From or Into for RawBytes, because it makes it harder to spot in reviews.
131// Instead, implement IntoDisk and FromDisk on the original type, or a specific wrapper type.
132
133impl RawBytes {
134    /// Create a new raw byte key or data.
135    ///
136    /// Mainly for use in tests or runtime checks.
137    /// These methods
138    pub fn new_raw_bytes(bytes: Vec<u8>) -> Self {
139        Self(bytes)
140    }
141
142    /// Create a new raw byte key or data.
143    /// Mainly for use in tests.
144    pub fn raw_bytes(&self) -> &Vec<u8> {
145        &self.0
146    }
147}
148
149impl IntoDisk for RawBytes {
150    type Bytes = Vec<u8>;
151
152    fn as_bytes(&self) -> Self::Bytes {
153        self.raw_bytes().clone()
154    }
155}
156
157impl FromDisk for RawBytes {
158    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
159        Self::new_raw_bytes(bytes.as_ref().to_vec())
160    }
161}
162
163// Serialization Modification Functions
164
165/// Truncates `mem_bytes` to `disk_len`, by removing zero bytes from the start of the slice.
166/// Used to discard unused zero bytes during serialization.
167///
168/// Return `None` if any of the truncated bytes are non-zero
169///
170/// # Panics
171///
172/// - if `mem_bytes` is shorter than `disk_len`.
173pub fn truncate_zero_be_bytes(mem_bytes: &[u8], disk_len: usize) -> Option<&[u8]> {
174    let discarded_bytes = mem_bytes
175        .len()
176        .checked_sub(disk_len)
177        .expect("unexpected `mem_bytes` length: must be at least `disk_len`");
178
179    if discarded_bytes == 0 {
180        return Some(mem_bytes);
181    }
182
183    let (discarded, truncated) = mem_bytes.split_at(discarded_bytes);
184
185    if !discarded.iter().all(|&byte| byte == 0) {
186        return None;
187    }
188
189    assert_eq!(truncated.len(), disk_len);
190
191    Some(truncated)
192}
193
194/// Expands `disk_bytes` to `mem_len`, by adding zero bytes at the start of the slice.
195/// Used to zero-fill bytes that were discarded during serialization.
196///
197/// # Panics
198///
199/// - if `disk_bytes` is longer than `mem_len`
200pub fn expand_zero_be_bytes(disk_bytes: &[u8], mem_len: usize) -> Vec<u8> {
201    let extra_bytes = mem_len
202        .checked_sub(disk_bytes.len())
203        .expect("unexpected `disk_bytes` length: must not exceed `mem_len`");
204
205    if extra_bytes == 0 {
206        return disk_bytes.to_vec();
207    }
208
209    let mut expanded = vec![0; extra_bytes];
210    expanded.extend(disk_bytes);
211
212    assert_eq!(expanded.len(), mem_len);
213
214    expanded
215}