1use cbor_core::Value;
4
5use crate::hash::Hash;
6#[cfg(any(test, feature = "test_utils"))]
7use crate::identity::SigningKey;
8use crate::identity::{Signature, VerifyingKey};
9use crate::logs::SeqNum;
10use crate::operation::{AnyHeader, Builder};
11use crate::traits::{Chain, Digest, Extensions, Offchain, Provenance};
12use crate::{Body, HeaderError};
13
14pub type Version = u16;
16
17pub type PayloadSize = u32;
19
20#[derive(Clone, Debug, PartialEq, Eq)]
41pub struct Header<E = ()> {
42 pub version: Version,
44
45 pub verifying_key: VerifyingKey,
47
48 pub signature: Signature,
50
51 pub payload_size: PayloadSize,
53
54 pub payload_hash: Option<Hash>,
60
61 pub seq_num: SeqNum,
64
65 pub backlink: Option<Hash>,
68
69 pub extensions: E,
80
81 pub(crate) extensions_cbor: Option<cbor_core::Value<'static>>,
87
88 pub(crate) size: u32,
90
91 pub(crate) digest: Hash,
93}
94
95impl<E> Header<E>
96where
97 E: Extensions,
98{
99 pub fn builder() -> Builder<E> {
101 Builder::new()
102 }
103
104 pub fn encode(&self) -> Vec<u8> {
106 encode_header(
107 self.version,
108 self.verifying_key,
109 Some(&self.signature),
110 self.payload_size,
111 self.payload_hash,
112 self.seq_num,
113 self.backlink,
114 self.extensions_cbor.as_ref(),
115 )
116 }
117
118 pub fn decode(bytes: &[u8]) -> Result<Self, HeaderError> {
122 let any_header = AnyHeader::decode(bytes)?;
124
125 Self::try_from(any_header)
127 }
128
129 pub fn hash(&self) -> Hash {
133 if cfg!(any(test, feature = "test_utils")) {
135 return Hash::digest(self.encode());
136 }
137
138 self.digest
139 }
140
141 pub fn size(&self) -> u32 {
143 if cfg!(any(test, feature = "test_utils")) {
145 return self.encode().len() as u32;
146 }
147
148 self.size
149 }
150}
151
152impl<E> Header<E> {
153 pub(crate) const fn has_zero_sized_extensions() -> bool {
154 std::mem::size_of::<E>() == 0
155 }
156
157 pub(crate) fn zero_sized_extensions() -> E {
158 assert!(Self::has_zero_sized_extensions());
159
160 unsafe { std::mem::zeroed() }
165 }
166}
167
168impl<E> Digest<Hash> for Header<E>
169where
170 E: Extensions,
171{
172 fn hash(&self) -> Hash {
173 self.hash()
174 }
175}
176
177impl<E> Provenance<VerifyingKey> for Header<E>
178where
179 E: Extensions,
180{
181 fn author(&self) -> VerifyingKey {
182 self.verifying_key
183 }
184
185 fn verify(&self) -> bool {
186 if cfg!(any(test, feature = "test_utils")) {
189 return self.verify();
190 }
191
192 true
194 }
195}
196
197impl<E> Chain<Hash> for Header<E>
198where
199 E: Extensions,
200{
201 fn backlink(&self) -> Option<Hash> {
202 self.backlink
203 }
204
205 fn seq_num(&self) -> SeqNum {
206 self.seq_num
207 }
208}
209
210impl<E> Offchain<Hash> for Header<E>
211where
212 E: Extensions,
213{
214 fn payload(&self) -> Option<&Body> {
215 None }
217
218 fn payload_hash(&self) -> Option<Hash> {
219 self.payload_hash
220 }
221
222 fn payload_size(&self) -> PayloadSize {
223 self.payload_size
224 }
225}
226
227#[allow(clippy::too_many_arguments)]
228pub(crate) fn encode_header(
229 version: Version,
230 verifying_key: VerifyingKey,
231 signature: Option<&Signature>,
232 payload_size: PayloadSize,
233 payload_hash: Option<Hash>,
234 seq_num: SeqNum,
235 backlink: Option<Hash>,
236 extensions: Option<&Value<'static>>,
237) -> Vec<u8> {
238 let mut cbor = Value::array([Value::from(version), Value::from(verifying_key.as_bytes())]);
239
240 if let Some(signature) = &signature {
242 cbor.append(signature.to_bytes());
243 }
244
245 cbor.append(payload_size);
246
247 if let Some(payload_hash) = &payload_hash {
248 cbor.append(payload_hash.as_bytes());
249 }
250
251 cbor.append(seq_num);
252
253 if let Some(backlink) = &backlink {
254 cbor.append(backlink.as_bytes());
255 }
256
257 if let Some(extensions) = extensions {
267 cbor.append(extensions.to_owned());
268 }
269
270 cbor.encode()
271}
272
273impl<E> TryFrom<AnyHeader> for Header<E>
274where
275 E: Extensions,
276{
277 type Error = HeaderError;
278
279 fn try_from(value: AnyHeader) -> Result<Self, Self::Error> {
280 let extensions = match value.extensions {
281 Some(ref cbor) => {
282 if Header::<E>::has_zero_sized_extensions() {
285 return Err(HeaderError::UnexpectedExtensions);
286 }
287
288 cbor.deserialized()
291 .map_err(HeaderError::DecodingExtensions)?
292 }
293 None => {
294 if !Header::<E>::has_zero_sized_extensions() {
295 return Err(HeaderError::MissingExtensions);
296 } else {
297 Header::<E>::zero_sized_extensions()
298 }
299 }
300 };
301
302 Ok(Header {
303 version: value.version,
304 verifying_key: value.verifying_key,
305 signature: value.signature,
306 payload_size: value.payload_size,
307 payload_hash: value.payload_hash,
308 seq_num: value.seq_num,
309 backlink: value.backlink,
310 extensions,
311 extensions_cbor: value.extensions,
312 size: value.size,
313 digest: value.digest,
314 })
315 }
316}
317
318#[cfg(any(test, feature = "test_utils"))]
319impl<E> Default for Header<E>
320where
321 E: Default,
322{
323 fn default() -> Self {
329 use crate::hash::HASH_LEN;
330 use crate::identity::SIGNATURE_LEN;
331
332 Self {
333 version: 1,
334 verifying_key: VerifyingKey::default(),
335 signature: Signature::from([0; SIGNATURE_LEN]),
336 payload_size: 0,
337 payload_hash: None,
338 seq_num: 0,
339 backlink: None,
340 extensions: E::default(),
341 extensions_cbor: None,
342 size: 0,
343 digest: Hash::from([0; HASH_LEN]),
344 }
345 }
346}
347
348#[cfg(any(test, feature = "test_utils"))]
349impl<E> Header<E>
350where
351 E: Extensions,
352{
353 pub fn to_hex(&self) -> String {
354 hex::encode(self.encode())
355 }
356
357 fn encode_signing_bytes(&self) -> Vec<u8> {
358 encode_header(
359 self.version,
360 self.verifying_key,
361 None,
362 self.payload_size,
363 self.payload_hash,
364 self.seq_num,
365 self.backlink,
366 self.extensions_cbor.as_ref(),
367 )
368 }
369
370 pub fn sign(&mut self, signer: &SigningKey) {
371 let signing_bytes = self.encode_signing_bytes();
372 self.signature = signer.sign(&signing_bytes);
373 self.update_size_and_digest();
374 }
375
376 pub fn verify(&self) -> bool {
377 let signing_bytes = self.encode_signing_bytes();
378 self.verifying_key.verify(&signing_bytes, &self.signature)
379 }
380
381 fn update_size_and_digest(&mut self) {
382 self.size = self.size();
383 self.digest = self.hash();
384 }
385}
386
387#[cfg(feature = "arbitrary")]
388impl<'a, E> arbitrary::Arbitrary<'a> for Header<E>
389where
390 E: Default + Extensions,
391{
392 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
393 use crate::hash::HASH_LEN;
394 use crate::identity::SIGNATURE_LEN;
395
396 let header = Header {
397 version: 1,
398 verifying_key: u.arbitrary()?,
399 signature: Signature::from_bytes(&[0; SIGNATURE_LEN]),
400 payload_size: u.arbitrary()?,
401 payload_hash: u.arbitrary()?,
402 seq_num: u.arbitrary()?,
403 backlink: u.arbitrary()?,
404 extensions: E::default(),
405 extensions_cbor: None,
406 size: 0,
407 digest: Hash::from_bytes([0; HASH_LEN]),
408 };
409
410 Ok(header)
411 }
412}
413
414#[cfg(test)]
415mod tests {
416 use super::Header;
417
418 #[test]
419 fn zst_size_matches_mem_checks() {
420 struct ZstExtensions;
421 assert_eq!(std::mem::size_of::<ZstExtensions>(), 0);
422 assert!(Header::<ZstExtensions>::has_zero_sized_extensions());
423
424 #[allow(unused)]
425 struct NonZstExtensions(u32);
426 assert_ne!(std::mem::size_of::<NonZstExtensions>(), 0);
427 assert!(!Header::<NonZstExtensions>::has_zero_sized_extensions());
428 }
429}