Skip to main content

zebra_state/service/finalized_state/disk_format/
shielded.rs

1//! Shielded transfer 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 bincode::Options;
9
10use zebra_chain::{
11    block::Height,
12    ironwood, orchard, sapling, sprout,
13    subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
14};
15
16use crate::service::finalized_state::disk_format::{FromDisk, IntoDisk};
17
18use super::block::HEIGHT_DISK_BYTES;
19
20impl IntoDisk for sprout::Nullifier {
21    type Bytes = [u8; 32];
22
23    fn as_bytes(&self) -> Self::Bytes {
24        *self.0
25    }
26}
27
28impl IntoDisk for sapling::Nullifier {
29    type Bytes = [u8; 32];
30
31    fn as_bytes(&self) -> Self::Bytes {
32        *self.0
33    }
34}
35
36impl IntoDisk for orchard::Nullifier {
37    type Bytes = [u8; 32];
38
39    fn as_bytes(&self) -> Self::Bytes {
40        let nullifier: orchard::Nullifier = *self;
41        nullifier.into()
42    }
43}
44
45impl IntoDisk for ironwood::Nullifier {
46    type Bytes = [u8; 32];
47
48    fn as_bytes(&self) -> Self::Bytes {
49        // The Ironwood nullifier set is stored under its own column family, disjoint from Orchard's,
50        // so it is safe to reuse the Orchard nullifier byte encoding.
51        (*self).into()
52    }
53}
54
55impl IntoDisk for sprout::tree::Root {
56    type Bytes = [u8; 32];
57
58    fn as_bytes(&self) -> Self::Bytes {
59        self.into()
60    }
61}
62
63impl FromDisk for sprout::tree::Root {
64    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
65        let array: [u8; 32] = bytes.as_ref().try_into().unwrap();
66        array.into()
67    }
68}
69
70impl IntoDisk for sapling::tree::Root {
71    type Bytes = [u8; 32];
72
73    fn as_bytes(&self) -> Self::Bytes {
74        self.into()
75    }
76}
77
78impl FromDisk for sapling::tree::Root {
79    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
80        let array: [u8; 32] = bytes.as_ref().try_into().unwrap();
81        array.try_into().expect("finalized data must be valid")
82    }
83}
84
85impl IntoDisk for orchard::tree::Root {
86    type Bytes = [u8; 32];
87
88    fn as_bytes(&self) -> Self::Bytes {
89        self.into()
90    }
91}
92
93impl FromDisk for orchard::tree::Root {
94    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
95        let array: [u8; 32] = bytes.as_ref().try_into().unwrap();
96        array.try_into().expect("finalized data must be valid")
97    }
98}
99
100impl IntoDisk for NoteCommitmentSubtreeIndex {
101    type Bytes = [u8; 2];
102
103    fn as_bytes(&self) -> Self::Bytes {
104        self.0.to_be_bytes()
105    }
106}
107
108impl FromDisk for NoteCommitmentSubtreeIndex {
109    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
110        let array: [u8; 2] = bytes.as_ref().try_into().unwrap();
111        Self(u16::from_be_bytes(array))
112    }
113}
114
115// The following implementations for the note commitment trees use `serde` and
116// `bincode`. `serde` serializations depend on the inner structure of the type.
117// They should not be used in new code. (This is an issue for any derived serialization format.)
118//
119// We explicitly use `bincode::DefaultOptions`  to disallow trailing bytes; see
120// https://docs.rs/bincode/1.3.3/bincode/config/index.html#options-struct-vs-bincode-functions
121
122impl IntoDisk for sprout::tree::NoteCommitmentTree {
123    type Bytes = Vec<u8>;
124
125    fn as_bytes(&self) -> Self::Bytes {
126        bincode::DefaultOptions::new()
127            .serialize(self)
128            .expect("serialization to vec doesn't fail")
129    }
130}
131
132impl FromDisk for sprout::tree::NoteCommitmentTree {
133    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
134        bincode::DefaultOptions::new()
135            .deserialize(bytes.as_ref())
136            .expect("deserialization format should match the serialization format used by IntoDisk")
137    }
138}
139impl IntoDisk for sapling::tree::NoteCommitmentTree {
140    type Bytes = Vec<u8>;
141
142    fn as_bytes(&self) -> Self::Bytes {
143        bincode::DefaultOptions::new()
144            .serialize(self)
145            .expect("serialization to vec doesn't fail")
146    }
147}
148
149impl FromDisk for sapling::tree::NoteCommitmentTree {
150    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
151        bincode::DefaultOptions::new()
152            .deserialize(bytes.as_ref())
153            .expect("deserialization format should match the serialization format used by IntoDisk")
154    }
155}
156
157impl IntoDisk for orchard::tree::NoteCommitmentTree {
158    type Bytes = Vec<u8>;
159
160    fn as_bytes(&self) -> Self::Bytes {
161        bincode::DefaultOptions::new()
162            .serialize(self)
163            .expect("serialization to vec doesn't fail")
164    }
165}
166
167impl FromDisk for orchard::tree::NoteCommitmentTree {
168    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
169        bincode::DefaultOptions::new()
170            .deserialize(bytes.as_ref())
171            .expect("deserialization format should match the serialization format used by IntoDisk")
172    }
173}
174
175impl IntoDisk for sapling_crypto::Node {
176    type Bytes = Vec<u8>;
177
178    fn as_bytes(&self) -> Self::Bytes {
179        self.to_bytes().to_vec()
180    }
181}
182
183impl IntoDisk for orchard::tree::Node {
184    type Bytes = Vec<u8>;
185
186    fn as_bytes(&self) -> Self::Bytes {
187        self.to_repr().to_vec()
188    }
189}
190
191impl<Root: IntoDisk<Bytes = Vec<u8>>> IntoDisk for NoteCommitmentSubtreeData<Root> {
192    type Bytes = Vec<u8>;
193
194    fn as_bytes(&self) -> Self::Bytes {
195        [self.end_height.as_bytes().to_vec(), self.root.as_bytes()].concat()
196    }
197}
198
199impl FromDisk for sapling_crypto::Node {
200    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
201        Self::from_bytes(
202            bytes
203                .as_ref()
204                .try_into()
205                .expect("trusted data should be 32 bytes"),
206        )
207        .expect("trusted data should deserialize successfully")
208    }
209}
210
211impl FromDisk for orchard::tree::Node {
212    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
213        Self::try_from(bytes.as_ref()).expect("trusted data should deserialize successfully")
214    }
215}
216
217impl<Node: FromDisk> FromDisk for NoteCommitmentSubtreeData<Node> {
218    fn from_bytes(disk_bytes: impl AsRef<[u8]>) -> Self {
219        let (height_bytes, node_bytes) = disk_bytes.as_ref().split_at(HEIGHT_DISK_BYTES);
220        Self::new(
221            Height::from_bytes(height_bytes),
222            Node::from_bytes(node_bytes),
223        )
224    }
225}