ozzy_replication/local/
configuration.rs1use super::Error;
2use crate::{Digest, Scope};
3use ozzy_proto::{GroupId, NodeId};
4
5pub const CONFIGURATION_BYTES: usize = 128;
7const DIGEST_START: usize = 88;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct Configuration {
13 scope: Scope,
14 broker: NodeId,
15 principal: Digest,
16}
17
18impl Configuration {
19 pub fn new(
21 group: GroupId,
22 epoch: u64,
23 broker: NodeId,
24 principal: Digest,
25 ) -> Result<Self, Error> {
26 if group.as_bytes() == &[0; 16]
27 || broker.as_bytes() == &[0; 16]
28 || epoch == 0
29 || principal == Digest::ZERO
30 {
31 return Err(Error::Configuration);
32 }
33 let mut configuration = Self {
34 scope: Scope {
35 group_id: group,
36 configuration_epoch: epoch,
37 configuration_digest: Digest::ZERO,
38 view: 0,
39 },
40 broker,
41 principal,
42 };
43 configuration.scope.configuration_digest = checksum(&configuration.encode());
44 Ok(configuration)
45 }
46
47 pub const fn scope(self) -> Scope {
49 self.scope
50 }
51 pub const fn broker(self) -> NodeId {
53 self.broker
54 }
55 pub const fn principal(self) -> Digest {
57 self.principal
58 }
59
60 pub fn encode(self) -> [u8; CONFIGURATION_BYTES] {
62 let mut bytes = [0; CONFIGURATION_BYTES];
63 bytes[..8].copy_from_slice(b"OZYLOCAL");
64 bytes[8..12].copy_from_slice(&[
65 ozzy_journal::integrity::PROFILE,
66 ozzy_proto::VERSION,
67 1,
68 1,
69 ]);
70 bytes[16..32].copy_from_slice(self.scope.group_id.as_bytes());
71 bytes[32..40].copy_from_slice(&self.scope.configuration_epoch.to_be_bytes());
72 bytes[40..56].copy_from_slice(self.broker.as_bytes());
73 bytes[56..88].copy_from_slice(self.principal.as_bytes());
74 bytes[DIGEST_START..120].copy_from_slice(self.scope.configuration_digest.as_bytes());
75 bytes
76 }
77
78 pub fn decode(bytes: &[u8]) -> Result<Self, Error> {
80 if bytes.len() != CONFIGURATION_BYTES {
81 return Err(Error::Encoding);
82 }
83 let configuration = Self::new(
84 GroupId::from_bytes(bytes[16..32].try_into().expect("fixed group")),
85 u64::from_be_bytes(bytes[32..40].try_into().expect("fixed epoch")),
86 NodeId::from_bytes(bytes[40..56].try_into().expect("fixed broker")),
87 Digest::from_bytes(bytes[56..88].try_into().expect("fixed principal")),
88 )?;
89 if bytes != configuration.encode() {
90 return Err(Error::Encoding);
91 }
92 Ok(configuration)
93 }
94}
95
96fn checksum(bytes: &[u8; CONFIGURATION_BYTES]) -> Digest {
97 let mut hasher =
98 ozzy_journal::integrity::IntegrityHasher::new("ozzy single broker configuration");
99 hasher.update(&bytes[..DIGEST_START]);
100 hasher.update(&[0; 32]);
101 hasher.update(&bytes[120..]);
102 hasher.finish()
103}