1use std::borrow::Borrow;
4use std::cell::RefCell;
5use std::collections::VecDeque;
6use std::marker::PhantomData;
7
8use p2panda_core::traits::Digest;
9use p2panda_core::{Extensions, Hash, Operation};
10use p2panda_store::Transaction;
11use p2panda_store::operations::OperationStore;
12use p2panda_store::orderer::OrdererStore;
13use p2panda_store::processor::ProcessorStore;
14use serde::{Deserialize, Serialize};
15use thiserror::Error;
16use tokio::sync::{Mutex, Notify};
17
18use crate::Processor;
19use crate::orderer::CausalOrderer;
20
21pub trait OrdererMetadata<E> {
22 type Metadata: Serialize + for<'de> Deserialize<'de>;
24
25 fn metadata(&self) -> Self::Metadata;
27
28 fn from_operation(operation: Operation<E>, meta: Self::Metadata) -> Self;
30}
31
32#[derive(Clone, Default, Debug, PartialEq, Eq)]
33pub enum OrdererArgs {
34 Process {
35 dependencies: Vec<Hash>,
36 },
37 #[default]
38 Ignore,
39}
40
41#[derive(Clone, Debug)]
42pub enum OrdererResult {
43 Pending,
45
46 ReadyInput { dependent_operations: Vec<Hash> },
50
51 ReadyOutput,
54
55 Ignored,
57}
58
59impl OrdererResult {
60 pub fn is_pending(&self) -> bool {
61 matches!(self, OrdererResult::Pending)
62 }
63}
64
65pub struct Orderer<S, T, E> {
66 inner: Mutex<CausalOrderer<Hash, S>>,
67 store: S,
68 notify: Notify,
69 queue: RefCell<VecDeque<(T, OrdererResult)>>,
79 _marker: PhantomData<E>,
80}
81
82impl<S, T, E> Orderer<S, T, E>
83where
84 S: Clone + Transaction + OrdererStore<Hash> + OperationStore<Operation<E>, Hash>,
85{
86 pub fn new(store: S) -> Self {
87 Self::from_namespace(store, "default")
88 }
89
90 pub fn from_namespace(store: S, namespace: impl AsRef<str>) -> Self {
91 let inner = CausalOrderer::new(store.clone()).with_namespace(namespace);
92
93 Self {
94 inner: Mutex::new(inner),
95 store,
96 notify: Notify::new(),
97 queue: RefCell::new(VecDeque::new()),
98 _marker: PhantomData,
99 }
100 }
101}
102
103impl<S, T, E> Processor<T> for Orderer<S, T, E>
104where
105 S: Transaction
106 + OrdererStore<Hash>
107 + OperationStore<Operation<E>, Hash>
108 + ProcessorStore<T::Metadata>,
109 T: OrdererMetadata<E> + Borrow<OrdererArgs> + Digest<Hash>,
110 E: Extensions,
111{
112 type Output = (T, OrdererResult);
113
114 type Error = (T, OrdererError);
115
116 async fn process(&self, input: T) -> Result<(), Self::Error> {
117 let args = input.borrow();
118
119 if let OrdererArgs::Process { dependencies } = args {
120 let inner = self.inner.lock().await;
121
122 let permit = match self.store.begin().await {
123 Ok(permit) => permit,
124 Err(err) => return Err((input, OrdererError::Transaction(err.to_string()))),
125 };
126
127 match inner.process(input.hash(), dependencies).await {
128 Ok(true) => {
130 let mut dependent_operations = Vec::new();
131
132 loop {
133 match inner.next().await {
134 Ok(Some(id)) => {
135 if id != input.hash() {
137 dependent_operations.push(id);
138 }
139
140 continue;
141 }
142 Ok(None) => {
143 break;
144 }
145 Err(err) => {
146 return Err((input, OrdererError::OrdererStore(err.to_string())));
147 }
148 }
149 }
150
151 let mut to_queue = Vec::new();
152
153 for id in &dependent_operations {
154 let operation = match self
155 .store
156 .get_operation_tx(id)
157 .await
158 .map_err(|err| OrdererError::OperationStore(err.to_string()))
159 {
160 Ok(Some(operation)) => operation,
161 Ok(None) => {
162 return Err((input, OrdererError::StoreInconsistency(*id)));
163 }
164 Err(err) => return Err((input, err)),
165 };
166
167 let metadata = match self.store.get_event_tx(&inner.namespace(), id).await {
168 Ok(Some(metadata)) => metadata,
169 Ok(None) => {
170 return Err((input, OrdererError::StoreInconsistency(*id)));
171 }
172 Err(err) => {
173 return Err((input, OrdererError::ProcessorStore(err.to_string())));
174 }
175 };
176
177 to_queue.push((
178 T::from_operation(operation, metadata),
179 OrdererResult::ReadyOutput,
180 ));
181 }
182
183 if let Err(err) = self.store.commit(permit).await {
184 return Err((input, OrdererError::Transaction(err.to_string())));
185 }
186
187 self.queue.borrow_mut().push_back((
189 input,
190 OrdererResult::ReadyInput {
191 dependent_operations,
192 },
193 ));
194
195 for item in to_queue {
197 self.queue.borrow_mut().push_back(item);
198 }
199 }
200
201 Ok(false) => {
203 if let Err(err) = self
204 .store
205 .set_event_tx(&inner.namespace(), &input.hash(), &input.metadata())
206 .await
207 {
208 return Err((input, OrdererError::ProcessorStore(err.to_string())));
209 };
210
211 if let Err(err) = self.store.commit(permit).await {
212 return Err((input, OrdererError::Transaction(err.to_string())));
213 }
214
215 self.queue
216 .borrow_mut()
217 .push_back((input, OrdererResult::Pending));
218 }
219 Err(err) => return Err((input, OrdererError::OrdererStore(err.to_string()))),
220 }
221 } else {
222 self.queue
223 .borrow_mut()
224 .push_back((input, OrdererResult::Ignored));
225 }
226
227 self.notify.notify_one(); Ok(())
230 }
231
232 async fn next(&self) -> Result<Self::Output, Self::Error> {
233 loop {
237 if let Some(output) = self.queue.borrow_mut().pop_front() {
238 return Ok(output);
239 }
240
241 self.notify.notified().await;
242 }
243 }
244}
245
246#[derive(Clone, Debug, Error)]
247pub enum OrdererError {
248 #[error("could not find item with id {0} in operation store")]
249 StoreInconsistency(Hash),
250
251 #[error("{0}")]
252 OrdererStore(String),
253
254 #[error("{0}")]
255 OperationStore(String),
256
257 #[error("{0}")]
258 ProcessorStore(String),
259
260 #[error("{0}")]
261 Transaction(String),
262}
263
264#[cfg(test)]
265mod tests {
266 use std::assert_matches;
267 use std::borrow::Borrow;
268
269 use futures_util::stream;
270 use p2panda_core::traits::Digest;
271 use p2panda_core::{Body, Hash, Header, Operation, SigningKey, Topic};
272 use p2panda_store::operations::OperationStore;
273 use p2panda_store::{SqliteStore, tx_unwrap};
274 use serde::{Deserialize, Serialize};
275 use tokio::task;
276 use tokio_stream::StreamExt;
277
278 use crate::{Processor, StreamLayerExt};
279
280 use super::{Orderer, OrdererArgs, OrdererMetadata, OrdererResult};
281
282 #[derive(Clone, Debug, Default, Serialize, Deserialize)]
283 struct TestExtension {
284 dependencies: Vec<Hash>,
285 }
286
287 #[derive(Clone, Debug, PartialEq, Eq)]
288 struct Event {
289 orderer_args: OrdererArgs,
290 operation: Operation<TestExtension>,
291 }
292
293 impl Borrow<OrdererArgs> for Event {
294 fn borrow(&self) -> &OrdererArgs {
295 &self.orderer_args
296 }
297 }
298
299 impl Digest<Hash> for Event {
300 fn hash(&self) -> Hash {
301 self.operation.hash
302 }
303 }
304
305 #[derive(Clone, Debug, Serialize, Deserialize)]
306 struct EventMetadata;
307
308 impl OrdererMetadata<TestExtension> for Event {
309 type Metadata = EventMetadata;
310
311 fn metadata(&self) -> Self::Metadata {
312 EventMetadata
313 }
314
315 #[allow(unused_variables)]
318 fn from_operation(operation: Operation<TestExtension>, meta: Self::Metadata) -> Self {
319 Self {
320 orderer_args: OrdererArgs::Process {
321 dependencies: operation.header.extensions.dependencies.clone(),
322 },
323 operation,
324 }
325 }
326 }
327
328 #[tokio::test]
329 async fn out_of_order() {
330 let operation_panda = {
333 let signing_key = SigningKey::generate();
334 let body: Body = b"Hi, Icebear".to_vec().into();
335
336 let header = Header::builder().body(&body).build(
337 &signing_key,
338 TestExtension {
339 dependencies: vec![],
340 },
341 );
342
343 Operation::from_parts(header, Some(body))
344 };
345
346 let event_panda = Event::from_operation(operation_panda.clone(), EventMetadata);
347
348 let operation_icebear = {
349 let signing_key = SigningKey::generate();
350 let body: Body = b"Hello, Pandasan!".to_vec().into();
351
352 let header = Header::builder().body(&body).build(
353 &signing_key,
354 TestExtension {
355 dependencies: vec![operation_panda.hash],
356 },
357 );
358
359 Operation::from_parts(header, Some(body))
360 };
361
362 let event_icebear = Event::from_operation(operation_icebear.clone(), EventMetadata);
363
364 let local = task::LocalSet::new();
365
366 local
367 .run_until(async move {
368 let store = SqliteStore::temporary().await;
369
370 tx_unwrap!(store, {
372 let log_id = Topic::random();
373
374 store
375 .insert_operation_tx(&operation_panda.hash, &operation_panda, &log_id)
376 .await
377 .unwrap();
378 store
379 .insert_operation_tx(&operation_icebear.hash, &operation_icebear, &log_id)
380 .await
381 .unwrap();
382 });
383
384 let orderer_1 = Orderer::from_namespace(store.clone(), "first");
386
387 let mut stream = stream::iter(vec![
388 event_icebear.clone(),
390 event_panda.clone(),
392 ])
393 .layer(orderer_1);
394
395 let (event, result) = stream.next().await.unwrap().unwrap();
398 assert!(result.is_pending());
399 assert_eq!(event, event_icebear);
400
401 let (event, result) = stream.next().await.unwrap().unwrap();
403 assert_matches!(
404 result,
405 OrdererResult::ReadyInput {
406 dependent_operations: _
407 }
408 );
409 assert_eq!(event, event_panda);
410
411 let (event, result) = stream.next().await.unwrap().unwrap();
413 assert_matches!(result, OrdererResult::ReadyOutput);
414 assert_eq!(event, event_icebear);
415
416 let orderer_2 = Orderer::from_namespace(store, "second");
418 orderer_2.process(event_icebear.clone()).await.unwrap();
419 let (_event, result) = orderer_2.next().await.unwrap();
420 assert_matches!(
421 result,
422 OrdererResult::Pending,
423 "panda's event was not processed yet for second orderer"
424 );
425 })
426 .await;
427 }
428}