1#![forbid(unsafe_code)]
2use std::io;
5
6use hop_actions::{apply_action, Action, ActionError};
7use hop_core::{CompanionMatrix, CoreError, Field, StreamGenerator};
8
9#[cfg(feature = "torsor_masking")]
10pub mod mask;
11pub mod proj;
12pub mod store;
13pub mod store_fs;
14
15use crate::proj::try_row0_of_poly;
16use store::{ChannelSnapshot, ChannelStore};
17
18#[derive(thiserror::Error, Debug)]
20#[non_exhaustive]
21pub enum ChannelError {
22 #[error("dimension mismatch: expected {expected}, got {actual}")]
24 DimensionMismatch { expected: usize, actual: usize },
25 #[error("invalid action: {0}")]
27 Action(#[from] ActionError),
28 #[error("invalid channel algebra: {0}")]
30 Core(#[from] CoreError),
31 #[error("channel storage failed: {0}")]
33 Storage(#[from] io::Error),
34 #[error("channel snapshot was not found")]
36 SnapshotNotFound,
37 #[error("snapshot channel identifier does not match requested identifier")]
39 SnapshotIdMismatch,
40 #[error("channel height exhausted")]
42 HeightOverflow,
43}
44
45pub struct Channel {
47 id: [u8; 32],
48 c: CompanionMatrix,
49 state: Vec<Field>,
50 height: u64,
51 store: Option<Box<dyn ChannelStore>>,
52}
53
54pub struct Producer;
56
57pub struct Subscriber {
59 row0: Vec<Field>,
60}
61
62impl Channel {
63 pub fn try_open(
65 c: CompanionMatrix,
66 init: Vec<Field>,
67 id: [u8; 32],
68 ) -> Result<Self, ChannelError> {
69 let expected = c.dimension();
70 if init.len() != expected {
71 return Err(ChannelError::DimensionMismatch {
72 expected,
73 actual: init.len(),
74 });
75 }
76 Ok(Self {
77 id,
78 c,
79 state: init,
80 height: 0,
81 store: None,
82 })
83 }
84
85 pub const fn id(&self) -> &[u8; 32] {
87 &self.id
88 }
89
90 pub const fn companion(&self) -> &CompanionMatrix {
92 &self.c
93 }
94
95 pub fn state(&self) -> &[Field] {
97 &self.state
98 }
99
100 pub fn dimension(&self) -> usize {
102 self.c.dimension()
103 }
104
105 pub const fn height(&self) -> u64 {
107 self.height
108 }
109
110 pub const fn has_store(&self) -> bool {
112 self.store.is_some()
113 }
114
115 pub fn with_store(mut self, store: Box<dyn ChannelStore>) -> Self {
117 self.store = Some(store);
118 self
119 }
120
121 pub fn snapshot(&self) -> ChannelSnapshot {
123 self.snapshot_with(&self.state, self.height)
124 }
125
126 pub const fn producer(&self) -> Producer {
128 Producer
129 }
130
131 pub fn try_subscribe_with_g(&self, g_coeffs: &[u64]) -> Result<Subscriber, ChannelError> {
133 let action = Action {
134 g_coeffs: g_coeffs.to_vec(),
135 nonce: 0,
136 mask_row0: None,
137 };
138 action.validate_for_dimension(self.dimension())?;
139 Ok(Subscriber {
140 row0: try_row0_of_poly(&self.c, g_coeffs)?,
141 })
142 }
143
144 pub fn apply(&mut self, action: &Action) -> Result<(), ChannelError> {
149 let next_state = apply_action(&self.c, &self.state, action)?;
150 let next_height = self
151 .height
152 .checked_add(1)
153 .ok_or(ChannelError::HeightOverflow)?;
154
155 if let Some(store) = &self.store {
156 store.append_action(&self.id, next_height, action)?;
157 }
158
159 self.state = next_state;
160 self.height = next_height;
161
162 Ok(())
163 }
164
165 pub fn save_now(&self) -> Result<(), ChannelError> {
167 if let Some(store) = &self.store {
168 store.save(&self.snapshot())?;
169 }
170 Ok(())
171 }
172
173 pub fn restore(id: [u8; 32], store: Box<dyn ChannelStore>) -> Result<Self, ChannelError> {
175 let snapshot = store.load(&id)?.ok_or(ChannelError::SnapshotNotFound)?;
176 if snapshot.id != id {
177 return Err(ChannelError::SnapshotIdMismatch);
178 }
179 let mut channel = Self::try_open(snapshot.c, snapshot.state, id)?;
180 channel.height = snapshot.height;
181
182 let actions = store.read_actions_from(&id, channel.height)?;
183 for action in actions {
184 channel.apply_without_persistence(&action)?;
185 }
186 channel.store = Some(store);
187 Ok(channel)
188 }
189
190 fn snapshot_with(&self, state: &[Field], height: u64) -> ChannelSnapshot {
191 ChannelSnapshot {
192 id: self.id,
193 c: self.c.clone(),
194 state: state.to_vec(),
195 height,
196 }
197 }
198
199 fn apply_without_persistence(&mut self, action: &Action) -> Result<(), ChannelError> {
200 let next_state = apply_action(&self.c, &self.state, action)?;
201 let next_height = self
202 .height
203 .checked_add(1)
204 .ok_or(ChannelError::HeightOverflow)?;
205 self.state = next_state;
206 self.height = next_height;
207 Ok(())
208 }
209}
210
211impl Subscriber {
212 pub fn row(&self) -> &[Field] {
214 &self.row0
215 }
216
217 pub fn project(&self, state: &[Field]) -> Result<Field, ChannelError> {
219 if state.len() != self.row0.len() {
220 return Err(ChannelError::DimensionMismatch {
221 expected: self.row0.len(),
222 actual: state.len(),
223 });
224 }
225 Ok(self
226 .row0
227 .iter()
228 .zip(state)
229 .fold(Field::zero(), |sum, (weight, value)| {
230 sum + (*weight * *value)
231 }))
232 }
233
234 pub fn stream(
236 &self,
237 mut stream: StreamGenerator,
238 n: usize,
239 ) -> Result<Vec<Field>, ChannelError> {
240 let dimension = stream.dimension();
241 if self.row0.len() != dimension {
242 return Err(ChannelError::DimensionMismatch {
243 expected: dimension,
244 actual: self.row0.len(),
245 });
246 }
247 let mut transform = vec![vec![Field::zero(); dimension]; dimension];
248 transform[0] = self.row0.clone();
249 stream.set_transform(transform)?;
250 Ok((0..n).map(|_| stream.step()).collect())
251 }
252}
253
254#[cfg(feature = "torsor_masking")]
255impl Subscriber {
256 pub fn twisted_row(
258 &self,
259 c: &CompanionMatrix,
260 nonce: u64,
261 degree: usize,
262 ) -> Result<Vec<Field>, crate::mask::MaskError> {
263 crate::mask::try_apply_twist_row(c, &self.row0, nonce, degree)
264 }
265}