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    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 sprout::tree::Root {
46    type Bytes = [u8; 32];
47
48    fn as_bytes(&self) -> Self::Bytes {
49        self.into()
50    }
51}
52
53impl FromDisk for sprout::tree::Root {
54    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
55        let array: [u8; 32] = bytes.as_ref().try_into().unwrap();
56        array.into()
57    }
58}
59
60impl IntoDisk for sapling::tree::Root {
61    type Bytes = [u8; 32];
62
63    fn as_bytes(&self) -> Self::Bytes {
64        self.into()
65    }
66}
67
68impl FromDisk for sapling::tree::Root {
69    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
70        let array: [u8; 32] = bytes.as_ref().try_into().unwrap();
71        array.try_into().expect("finalized data must be valid")
72    }
73}
74
75impl IntoDisk for orchard::tree::Root {
76    type Bytes = [u8; 32];
77
78    fn as_bytes(&self) -> Self::Bytes {
79        self.into()
80    }
81}
82
83impl FromDisk for orchard::tree::Root {
84    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
85        let array: [u8; 32] = bytes.as_ref().try_into().unwrap();
86        array.try_into().expect("finalized data must be valid")
87    }
88}
89
90impl IntoDisk for NoteCommitmentSubtreeIndex {
91    type Bytes = [u8; 2];
92
93    fn as_bytes(&self) -> Self::Bytes {
94        self.0.to_be_bytes()
95    }
96}
97
98impl FromDisk for NoteCommitmentSubtreeIndex {
99    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
100        let array: [u8; 2] = bytes.as_ref().try_into().unwrap();
101        Self(u16::from_be_bytes(array))
102    }
103}
104
105// The following implementations for the note commitment trees use `serde` and
106// `bincode`. `serde` serializations depend on the inner structure of the type.
107// They should not be used in new code. (This is an issue for any derived serialization format.)
108//
109// We explicitly use `bincode::DefaultOptions`  to disallow trailing bytes; see
110// https://docs.rs/bincode/1.3.3/bincode/config/index.html#options-struct-vs-bincode-functions
111
112impl IntoDisk for sprout::tree::NoteCommitmentTree {
113    type Bytes = Vec<u8>;
114
115    fn as_bytes(&self) -> Self::Bytes {
116        bincode::DefaultOptions::new()
117            .serialize(self)
118            .expect("serialization to vec doesn't fail")
119    }
120}
121
122impl FromDisk for sprout::tree::NoteCommitmentTree {
123    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
124        bincode::DefaultOptions::new()
125            .deserialize(bytes.as_ref())
126            .expect("deserialization format should match the serialization format used by IntoDisk")
127    }
128}
129impl IntoDisk for sapling::tree::NoteCommitmentTree {
130    type Bytes = Vec<u8>;
131
132    fn as_bytes(&self) -> Self::Bytes {
133        bincode::DefaultOptions::new()
134            .serialize(self)
135            .expect("serialization to vec doesn't fail")
136    }
137}
138
139impl FromDisk for sapling::tree::NoteCommitmentTree {
140    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
141        bincode::DefaultOptions::new()
142            .deserialize(bytes.as_ref())
143            .expect("deserialization format should match the serialization format used by IntoDisk")
144    }
145}
146
147impl IntoDisk for orchard::tree::NoteCommitmentTree {
148    type Bytes = Vec<u8>;
149
150    fn as_bytes(&self) -> Self::Bytes {
151        bincode::DefaultOptions::new()
152            .serialize(self)
153            .expect("serialization to vec doesn't fail")
154    }
155}
156
157impl FromDisk for orchard::tree::NoteCommitmentTree {
158    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
159        bincode::DefaultOptions::new()
160            .deserialize(bytes.as_ref())
161            .expect("deserialization format should match the serialization format used by IntoDisk")
162    }
163}
164
165impl IntoDisk for sapling::tree::Node {
166    type Bytes = Vec<u8>;
167
168    fn as_bytes(&self) -> Self::Bytes {
169        self.as_ref().to_vec()
170    }
171}
172
173impl IntoDisk for orchard::tree::Node {
174    type Bytes = Vec<u8>;
175
176    fn as_bytes(&self) -> Self::Bytes {
177        self.to_repr().to_vec()
178    }
179}
180
181impl<Root: IntoDisk<Bytes = Vec<u8>>> IntoDisk for NoteCommitmentSubtreeData<Root> {
182    type Bytes = Vec<u8>;
183
184    fn as_bytes(&self) -> Self::Bytes {
185        [self.end_height.as_bytes().to_vec(), self.root.as_bytes()].concat()
186    }
187}
188
189impl FromDisk for sapling::tree::Node {
190    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
191        Self::try_from(bytes.as_ref()).expect("trusted data should deserialize successfully")
192    }
193}
194
195impl FromDisk for orchard::tree::Node {
196    fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
197        Self::try_from(bytes.as_ref()).expect("trusted data should deserialize successfully")
198    }
199}
200
201impl<Node: FromDisk> FromDisk for NoteCommitmentSubtreeData<Node> {
202    fn from_bytes(disk_bytes: impl AsRef<[u8]>) -> Self {
203        let (height_bytes, node_bytes) = disk_bytes.as_ref().split_at(HEIGHT_DISK_BYTES);
204        Self::new(
205            Height::from_bytes(height_bytes),
206            Node::from_bytes(node_bytes),
207        )
208    }
209}