1use std::marker::PhantomData;
4
5use p2panda_core::traits::{Digest, OperationId};
6use p2panda_store::Transaction;
7use p2panda_store::operations::OperationStore;
8use p2panda_store::orderer::OrdererStore;
9use thiserror::Error;
10use tokio::sync::{Mutex, Notify};
11
12use crate::orderer::{CausalOrderer, Ordering};
13use crate::processors::Processor;
14
15pub struct Orderer<T, ID, S> {
16 inner: Mutex<CausalOrderer<ID, S>>,
17 store: S,
18 notify: Notify,
19 _marker: PhantomData<T>,
20}
21
22impl<T, ID, S> Orderer<T, ID, S>
23where
24 ID: OperationId,
25 S: Clone + Transaction + OrdererStore<ID> + OperationStore<T, ID>,
26{
27 pub fn new(store: S) -> Self {
28 let inner = CausalOrderer::new(store.clone());
29
30 Self {
31 inner: Mutex::new(inner),
32 store,
33 notify: Notify::new(),
34 _marker: PhantomData,
35 }
36 }
37}
38
39impl<T, ID, S> Processor<T> for Orderer<T, ID, S>
40where
41 T: Digest<ID> + Ordering<ID>,
42 ID: OperationId,
43 S: Transaction + OrdererStore<ID> + OperationStore<T, ID>,
44{
45 type Output = T;
46
47 type Error = (Option<T>, OrdererError<T, ID, S>);
48
49 async fn process(&self, input: T) -> Result<(), Self::Error> {
50 let permit = match self.store.begin().await {
51 Ok(permit) => permit,
52 Err(err) => return Err((Some(input), OrdererError::Transaction(err))),
53 };
54
55 let inner = self.inner.lock().await;
56 if let Err(err) = inner.process(input.hash(), input.dependencies()).await {
57 return Err((Some(input), OrdererError::OrdererStore(err)));
58 };
59
60 self.store
61 .commit(permit)
62 .await
63 .map_err(|err| (Some(input), OrdererError::Transaction(err)))?;
64
65 self.notify.notify_one(); Ok(())
68 }
69
70 async fn next(&self) -> Result<Self::Output, Self::Error> {
71 loop {
72 let inner = self.inner.lock().await;
73
74 let permit = self
75 .store
76 .begin()
77 .await
78 .map_err(|err| (None, OrdererError::Transaction(err)))?;
79
80 if let Some(id) = inner
81 .next()
82 .await
83 .map_err(|err| (None, OrdererError::OrdererStore(err)))?
84 {
85 self.store
86 .commit(permit)
87 .await
88 .map_err(|err| (None, OrdererError::Transaction(err)))?;
89
90 return match self
91 .store
92 .get_operation(&id)
93 .await
94 .map_err(OrdererError::OperationStore)
95 {
96 Ok(Some(operation)) => Ok(operation),
97 Ok(None) => Err((None, OrdererError::StoreInconsistency(id))),
98 Err(err) => Err((None, err)),
99 };
100 }
101
102 self.notify.notified().await;
103 }
104 }
105}
106
107#[derive(Debug, Error)]
108pub enum OrdererError<T, ID, S>
109where
110 T: Ordering<ID>,
111 ID: OperationId,
112 S: Transaction + OrdererStore<ID> + OperationStore<T, ID>,
113{
114 #[error("could not find item with id {0} in operation store")]
115 StoreInconsistency(ID),
116
117 #[error("{0}")]
118 OrdererStore(<S as OrdererStore<ID>>::Error),
119
120 #[error("{0}")]
121 OperationStore(<S as OperationStore<T, ID>>::Error),
122
123 #[error("{0}")]
124 Transaction(<S as Transaction>::Error),
125}
126
127#[cfg(test)]
128mod tests {
129 use futures_util::stream;
130 use p2panda_core::{Body, Hash, Header, Operation, SigningKey, Topic};
131 use p2panda_store::operations::OperationStore;
132 use p2panda_store::{SqliteStore, tx_unwrap};
133 use serde::{Deserialize, Serialize};
134 use tokio::task;
135 use tokio_stream::StreamExt;
136
137 use crate::StreamLayerExt;
138
139 use super::{Orderer, Ordering};
140
141 #[derive(Clone, Debug, Default, Serialize, Deserialize)]
142 struct TestExtension {
143 dependencies: Vec<Hash>,
144 }
145
146 impl Ordering<Hash> for Operation<TestExtension> {
147 fn dependencies(&self) -> &[Hash] {
148 &self.header.extensions.dependencies
149 }
150 }
151
152 #[tokio::test]
153 async fn out_of_order() {
154 let operation_panda = {
157 let signing_key = SigningKey::generate();
158 let verifying_key = signing_key.verifying_key();
159
160 let body: Body = b"Hi, Icebear".to_vec().into();
161
162 let mut header = Header {
163 verifying_key,
164 payload_size: body.size(),
165 payload_hash: Some(body.hash()),
166 extensions: TestExtension {
167 dependencies: vec![],
168 },
169 ..Default::default()
170 };
171 header.sign(&signing_key);
172
173 Operation {
174 hash: header.hash(),
175 header,
176 body: Some(body),
177 }
178 };
179
180 let operation_icebear = {
181 let signing_key = SigningKey::generate();
182 let verifying_key = signing_key.verifying_key();
183
184 let body: Body = b"Hello, Pandasan!".to_vec().into();
185
186 let mut header = Header {
187 verifying_key,
188 payload_size: body.size(),
189 payload_hash: Some(body.hash()),
190 extensions: TestExtension {
191 dependencies: vec![operation_panda.hash],
192 },
193 ..Default::default()
194 };
195 header.sign(&signing_key);
196
197 Operation {
198 hash: header.hash(),
199 header,
200 body: Some(body),
201 }
202 };
203
204 let local = task::LocalSet::new();
205
206 local
207 .run_until(async move {
208 let store = SqliteStore::temporary().await;
209
210 tx_unwrap!(store, {
212 let log_id = Topic::random();
213
214 store
215 .insert_operation(&operation_panda.hash, &operation_panda, &log_id)
216 .await
217 .unwrap();
218 store
219 .insert_operation(&operation_icebear.hash, &operation_icebear, &log_id)
220 .await
221 .unwrap();
222 });
223
224 let orderer = Orderer::new(store);
226
227 let mut stream = stream::iter(vec![
228 operation_icebear.clone(),
230 operation_panda.clone(),
232 ])
233 .layer(orderer);
234
235 let operation = stream.next().await.unwrap().unwrap();
236 assert_eq!(operation, operation_panda);
237
238 let operation = stream.next().await.unwrap().unwrap();
239 assert_eq!(operation, operation_icebear);
240 })
241 .await;
242 }
243}