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