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