1use crate::admin::{SnapshotId, StoreChange};
4use crate::error::{require, Error, Rule};
5use crate::path::ObjectPath;
6use crate::value::{positive, Slot};
7use crate::wire::{decode_frame, decode_frame_value, wire_struct, Decoder, Encoder, Wire};
8use coven_foundation::id_source::{DeviceId, MemberId};
9
10pub const MAX_BOUNDED_SLOT: usize = 4 * 1024 * 1024;
12
13#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct SlotHeader {
16 pub device: DeviceId,
18 pub claim: u64,
20 pub author: MemberId,
22 pub after: u64,
24}
25wire_struct!(SlotHeader, device, claim, author, after);
26impl SlotHeader {
27 pub const ENCODED_LEN: usize = crate::FRAME_PREFIX_LEN + 8 + 8 + 16 + 8;
30
31 pub fn encode(&self, path: &ObjectPath) -> Result<Vec<u8>, Error> {
33 self.validate_at(path)?;
34 crate::encode_frame(crate::kinds::SLOT_HEADER, self)
35 }
36
37 pub fn decode(bytes: &[u8], path: &ObjectPath) -> Result<Self, Error> {
39 let header: Self = decode_frame_value(bytes, crate::kinds::SLOT_HEADER, "slot header")?;
40 header.validate_at(path)?;
41 Ok(header)
42 }
43
44 pub fn validate_at(&self, path: &ObjectPath) -> Result<(), Error> {
46 positive(self.claim)?;
47 require(
48 path.slot_number().is_some_and(|slot| slot.get() - 1 == self.after),
49 "slot predecessor",
50 Rule::Coverage,
51 )
52 }
53
54 pub fn check_write(&self, write: &crate::write::WriteHeader) -> Result<(), Error> {
57 write.validate()?;
58 require(write.position.device == self.device, "slot write device", Rule::WriteDevice)?;
59 require(write.read_through <= self.after, "slot write read-through", Rule::Coverage)
60 }
61}
62
63#[derive(Clone, Copy, Debug, PartialEq, Eq)]
65pub enum Purpose {
66 Ordinary,
68 Raise(u32),
70 Reset,
72}
73impl Purpose {
74 pub(crate) fn validate(&self) -> Result<(), Error> {
75 if let Self::Raise(version) = self {
76 require(*version > 0, "snapshot raise version", Rule::RaiseVersion)?;
77 }
78 Ok(())
79 }
80}
81impl Wire for Purpose {
82 fn put(&self, out: &mut Encoder) -> Result<(), Error> {
83 self.validate()?;
84 match self {
85 Self::Ordinary => 0u8.put(out),
86 Self::Raise(version) => {
87 1u8.put(out)?;
88 version.put(out)
89 }
90 Self::Reset => 2u8.put(out),
91 }
92 }
93 fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
94 let purpose = match u8::get(input)? {
95 0 => Self::Ordinary,
96 1 => Self::Raise(u32::get(input)?),
97 2 => Self::Reset,
98 tag => return Err(Error::UnknownTag { field: "snapshot purpose", tag }),
99 };
100 purpose.validate()?;
101 Ok(purpose)
102 }
103}
104
105#[derive(Clone, Debug, PartialEq, Eq)]
107pub struct SnapshotAnnouncement {
108 pub snapshot: SnapshotId,
110 pub covers: u64,
112 pub breaking_version: u32,
116 pub purpose: Purpose,
118 pub digest: [u8; 32],
120}
121wire_struct!(SnapshotAnnouncement, snapshot, covers, breaking_version, purpose, digest);
122impl SnapshotAnnouncement {
123 pub fn encode(&self, slot: Slot) -> Result<Vec<u8>, Error> {
125 self.validate_at(slot)?;
126 crate::encode_frame(crate::kinds::SNAPSHOT_ANNOUNCEMENT, self)
127 }
128 pub fn decode(bytes: &[u8], slot: Slot) -> Result<Self, Error> {
130 let announcement: Self = decode_frame_value(
131 bytes,
132 crate::kinds::SNAPSHOT_ANNOUNCEMENT,
133 "snapshot announcement",
134 )?;
135 announcement.validate_at(slot)?;
136 Ok(announcement)
137 }
138 fn validate_at(&self, slot: Slot) -> Result<(), Error> {
139 positive(self.snapshot.number)?;
140 require(self.covers < slot.get(), "announcement coverage", Rule::Coverage)?;
141 self.purpose.validate()
142 }
143}
144
145#[derive(Clone, Debug, PartialEq, Eq)]
147pub enum SlotFrame {
148 Admin(StoreChange),
150 KeyCopy,
152 Announcement(SnapshotAnnouncement),
154}
155impl SlotFrame {
156 pub fn encode(&self, header: &SlotHeader, path: &ObjectPath) -> Result<Vec<u8>, Error> {
158 let mut bytes = header.encode(path)?;
159 let slot = path.slot_number().expect("header checked slot path");
160 let frame = match self {
161 Self::Admin(change) => {
162 check_admin(change, header, path)?;
163 change.encode()?
164 }
165 Self::KeyCopy => crate::encode_frame_with(crate::kinds::KEY_COPY_ACK, |_| Ok(()))?,
166 Self::Announcement(announcement) => announcement.encode(slot)?,
167 };
168 bytes.extend_from_slice(&frame);
169 Ok(bytes)
170 }
171
172 pub fn decode(bytes: &[u8], path: &ObjectPath) -> Result<(SlotHeader, Self), Error> {
174 let length = crate::frame_length(bytes)?;
175 let header = SlotHeader::decode(bytes.get(..length).ok_or(Error::Truncated)?, path)?;
176 let frame = bytes.get(length..).ok_or(Error::Truncated)?;
177 let (kind, input) = decode_frame(frame)?;
178 let body = match kind {
179 crate::kinds::ADMIN => {
180 let change = StoreChange::decode(frame)?;
181 check_admin(&change, &header, path)?;
182 Self::Admin(change)
183 }
184 crate::kinds::SNAPSHOT_ANNOUNCEMENT => Self::Announcement(
185 SnapshotAnnouncement::decode(frame, path.slot_number().expect("checked path"))?,
186 ),
187 crate::kinds::KEY_COPY_ACK => {
188 input.finish()?;
189 Self::KeyCopy
190 }
191 tag => return Err(Error::UnknownTag { field: "slot frame", tag }),
192 };
193 Ok((header, body))
194 }
195}
196
197fn check_admin(change: &StoreChange, header: &SlotHeader, path: &ObjectPath) -> Result<(), Error> {
198 match change {
199 StoreChange::CreateStore { store, .. } => {
200 require(header.after == 0 && *store == path.store(), "creation slot", Rule::Store)
201 }
202 StoreChange::AddDevice { replaces, .. } => {
203 require(*replaces != Some(header.device), "replacement device", Rule::DeviceReplacement)
204 }
205 StoreChange::AdmitMember { device, .. } | StoreChange::RecoverMember { device, .. } => {
206 require(*device != header.device, "admitted device", Rule::AdmittedDevice)
207 }
208 StoreChange::RaiseSchema { announcement, .. } | StoreChange::Reset { announcement } => {
209 require(announcement.get() <= header.after, "activation announcement", Rule::Coverage)
210 }
211 _ => Ok(()),
212 }
213}
214
215#[cfg(test)]
216#[path = "sequence_tests.rs"]
217mod tests;