dig_network_block/
header.rs1use crate::dig_l2_definition as definitions;
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10
11#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
13pub struct L2BlockHeader {
14 pub version: u32,
16 #[serde(with = "crate::serde_hex::hex32")]
18 pub network_id: [u8; 32],
19 pub epoch: u64,
21 #[serde(with = "crate::serde_hex::hex32")]
23 pub prev_block_root: [u8; 32],
24 #[serde(with = "crate::serde_hex::hex32")]
26 pub body_root: [u8; 32],
27 pub data_count: u32,
29 pub emissions_count: u32,
31 #[serde(with = "crate::serde_hex::hex48")]
33 pub proposer_pubkey: [u8; 48],
34}
35
36impl L2BlockHeader {
37 pub fn calculate_root(&self) -> definitions::Hash32 {
39 definitions::COMPUTE_HEADER_ROOT(self)
40 }
41
42 pub fn validate_version(&self, expected_version: u32) -> Result<(), HeaderError> {
44 if self.version != expected_version {
45 return Err(HeaderError::VersionMismatch {
46 expected: expected_version,
47 found: self.version,
48 });
49 }
50 Ok(())
51 }
52
53 pub fn validate_counts(
55 &self,
56 data_len: usize,
57 emissions_len: usize,
58 ) -> Result<(), HeaderError> {
59 if self.data_count as usize != data_len {
60 return Err(HeaderError::CountMismatch {
61 field: "data_count",
62 expected: self.data_count as usize,
63 actual: data_len,
64 });
65 }
66 if self.emissions_count as usize != emissions_len {
67 return Err(HeaderError::CountMismatch {
68 field: "emissions_count",
69 expected: self.emissions_count as usize,
70 actual: emissions_len,
71 });
72 }
73 Ok(())
74 }
75}
76
77#[derive(Debug, Error)]
79pub enum HeaderError {
80 #[error("version mismatch: expected {expected}, found {found}")]
82 VersionMismatch { expected: u32, found: u32 },
83
84 #[error("{field} mismatch: header has {expected}, body has {actual}")]
86 CountMismatch {
87 field: &'static str,
88 expected: usize,
89 actual: usize,
90 },
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 fn sample_header() -> L2BlockHeader {
98 L2BlockHeader {
99 version: 1,
100 network_id: [1u8; 32],
101 epoch: 10,
102 prev_block_root: [2u8; 32],
103 body_root: [3u8; 32],
104 data_count: 2,
105 emissions_count: 1,
106 proposer_pubkey: [9u8; 48],
107 }
108 }
109
110 #[test]
111 fn header_root_changes_when_field_changes() {
112 let h1 = sample_header();
113 let mut h2 = sample_header();
114 assert_eq!(h1.calculate_root(), h2.calculate_root());
115 h2.data_count = 3;
116 assert_ne!(h1.calculate_root(), h2.calculate_root());
117 }
118
119 #[test]
120 fn version_validation() {
121 let h = sample_header();
122 assert!(h.validate_version(1).is_ok());
123 let e = h.validate_version(2).unwrap_err();
124 match e {
125 HeaderError::VersionMismatch { expected, found } => {
126 assert_eq!(expected, 2);
127 assert_eq!(found, 1);
128 }
129 _ => panic!("unexpected error variant"),
130 }
131 }
132
133 #[test]
134 fn counts_validation() {
135 let h = sample_header();
136 assert!(h.validate_counts(2, 1).is_ok());
137 let e = h.validate_counts(1, 1).unwrap_err();
138 match e {
139 HeaderError::CountMismatch {
140 field,
141 expected,
142 actual,
143 } => {
144 assert_eq!(field, "data_count");
145 assert_eq!(expected, 2);
146 assert_eq!(actual, 1);
147 }
148 _ => panic!("unexpected error variant"),
149 }
150 }
151}