1use ossa_crdt::time::CausalState;
2use ossa_crdt::CRDT;
5use ossa_typeable::Typeable;
6use serde::{Deserialize, Serialize};
7use std::collections::{BTreeMap, BTreeSet};
8use std::fmt::{Debug, Display};
9use std::marker::PhantomData;
10use std::net::{Ipv4Addr, SocketAddrV4};
11use std::sync::Arc;
12use std::thread;
13use tokio::net::{TcpListener, TcpStream};
14use tokio::runtime::Runtime;
15use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
16use tokio::sync::{watch, RwLock};
17use tokio::task::JoinHandle;
18use tokio_util::codec::{self, LengthDelimitedCodec};
19use tracing::{debug, error, info, warn};
20
21use crate::auth::{generate_identity, DeviceId, Identity};
22use crate::network::protocol::{run_handshake_client, run_handshake_server, HandshakeError};
23use crate::protocol::manager::v0::PeerManagerCommand;
24use crate::protocol::MiniProtocolArgs;
25use crate::storage::Storage;
26use crate::store::ecg::{self, ECGBody, ECGHeader};
27use crate::store::{self, StateUpdate, StoreCommand, UntypedStoreCommand};
28use crate::time::ConcretizeTime;
29use crate::util::{self, TypedStream};
30
31pub struct Ossa<OT: OssaType> {
32 thread: thread::JoinHandle<()>,
34 tokio_runtime: Runtime,
36 active_stores: watch::Sender<
39 StoreStatuses<OT::StoreId, OT::Hash, <OT::ECGHeader as ECGHeader>::HeaderId, OT::ECGHeader>,
40 >, shared_state: SharedState<OT::StoreId>, phantom: PhantomData<OT>,
44 identity_keys: Identity,
45}
46pub type StoreStatuses<StoreId, Hash, HeaderId, Header> =
47 BTreeMap<StoreId, StoreStatus<Hash, HeaderId, Header>>; pub enum StoreStatus<Hash, HeaderId, Header> {
53 Initializing,
55 Running {
57 store_handle: JoinHandle<()>, send_command_chan: UnboundedSender<UntypedStoreCommand<Hash, HeaderId, Header>>,
65 },
66}
67
68#[derive(Clone, Debug)]
69pub(crate) struct SharedState<StoreId> {
71 pub(crate) peer_state:
72 Arc<RwLock<BTreeMap<DeviceId, UnboundedSender<PeerManagerCommand<StoreId>>>>>,
73}
74
75impl<Hash, HeaderId, Header> StoreStatus<Hash, HeaderId, Header> {
76 pub(crate) fn is_initializing(&self) -> bool {
77 match self {
78 StoreStatus::Initializing => true,
79 StoreStatus::Running { .. } => false,
80 }
81 }
82
83 pub(crate) fn is_initialized(&self) -> bool {
84 !self.is_initializing()
85 }
86
87 pub(crate) fn command_channel(
88 &self,
89 ) -> Option<&UnboundedSender<UntypedStoreCommand<Hash, HeaderId, Header>>> {
90 match self {
91 StoreStatus::Initializing => None,
92 StoreStatus::Running {
93 send_command_chan, ..
94 } => Some(send_command_chan),
95 }
96 }
97}
98
99impl<OT: OssaType> Ossa<OT> {
100 async fn bind_server_ipv4(mut port: u16) -> Option<TcpListener> {
101 for _ in 0..10 {
102 let address = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port);
103 match TcpListener::bind(&address).await {
104 Ok(l) => {
105 info!("Started server: {address}");
106 return Some(l);
107 }
108 Err(err) => {
109 warn!("Failed to bind to port ({}): {}", &address, err);
110 port += 1;
111 }
112 }
113 }
114
115 None
116 }
117
118 pub fn start(config: OssaConfig) -> Self {
120 let identity_keys = generate_identity();
122
123 let (active_stores, active_stores_receiver) = watch::channel(BTreeMap::new());
126 let device_id = DeviceId::new(identity_keys.auth_key().verifying_key());
127
128 let shared_state_ = SharedState {
129 peer_state: Arc::new(RwLock::new(BTreeMap::new())),
130 };
131
132 let runtime = match tokio::runtime::Runtime::new() {
134 Ok(r) => r,
135 Err(err) => {
136 error!("Failed to initialize tokio runtime: {}", err);
137 todo!()
138 }
139 };
140 let runtime_handle = runtime.handle().clone();
141 let shared_state = shared_state_.clone();
142
143 let ossa_thread = thread::spawn(move || {
145 runtime_handle.block_on(async move {
146 let Some(listener) = Ossa::<OT>::bind_server_ipv4(config.port).await else {
148 error!("Failed to start server.");
149 return;
150 };
151
152 info!("Starting server");
162 loop {
163 let (tcpstream, peer) = match listener.accept().await {
165 Ok(r) => r,
166 Err(err) => {
167 error!("Failed to accept connection: {}", err);
168 continue;
169 }
170 };
171 info!("Accepted connection from peer: {}", peer);
172 let active_stores = active_stores_receiver.clone();
174 let shared_state = shared_state.clone();
176
177 let future_handle = tokio::spawn(async move {
178 let stream = codec::Framed::new(tcpstream, LengthDelimitedCodec::new());
180
181 let mut stream = TypedStream::new(stream);
186 let handshake_result = run_handshake_server(&mut stream, &device_id).await;
187 let stream = stream.finalize().into_inner();
188
189 let handshake_result = match handshake_result {
190 Ok(r) => r,
191 Err(HandshakeError::ConnectingToSelf) => {
192 info!("Disconnecting. Attempting to connect to ourself.");
193 return;
194 }
195 };
196
197 info!(
198 "Handshake complete with peer: {}",
199 handshake_result.peer_id()
200 );
201 if let Some(recv) =
203 initiate_peer(handshake_result.peer_id(), &shared_state).await
204 {
205 let args = MiniProtocolArgs::new(
207 handshake_result.peer_id(),
208 active_stores,
209 recv,
210 );
211 handshake_result
212 .version()
213 .run_miniprotocols_server::<OT>(stream, args)
214 .await;
215 } else {
216 info!(
217 "Disconnecting. Already connected to peer: {}",
218 handshake_result.peer_id()
219 );
220 }
221 });
222 }
223 });
224 });
225
226 Ossa {
229 thread: ossa_thread,
230 tokio_runtime: runtime,
232 active_stores,
233 phantom: PhantomData,
234 shared_state: shared_state_,
235 identity_keys,
236 }
237 }
238
239 pub fn create_store<T, S: Storage>(&self, initial_state: T, _storage: S) -> StoreHandle<OT, T>
240 where
241 T: CRDT<Time = OT::Time>
242 + Clone
243 + Debug
244 + Send
245 + 'static
246 + Typeable
247 + Serialize
248 + for<'d> Deserialize<'d>,
249 T::Op: ConcretizeTime<<OT::ECGHeader as ECGHeader>::HeaderId>,
252 OT::ECGBody<T>: Send
253 + Serialize
254 + for<'d> Deserialize<'d>
255 + Debug
256 + ECGBody<
257 T::Op,
258 <T::Op as ConcretizeTime<<OT::ECGHeader as ECGHeader>::HeaderId>>::Serialized,
259 Header = OT::ECGHeader,
260 >,
261 OT::ECGHeader: Send + Sync + Clone + 'static + Serialize + for<'d> Deserialize<'d>,
262 <OT::ECGHeader as ECGHeader>::HeaderId: Send + Serialize + for<'d> Deserialize<'d>,
265 {
266 let store = store::State::<OT::StoreId, OT::ECGHeader, T, OT::Hash>::new_syncing(
268 initial_state.clone(),
269 );
270 let store_id = store.store_id();
271
272 let mut already_exists = false;
275 self.active_stores.send_if_modified(|active_stores| {
276 let res = active_stores.try_insert(store_id, StoreStatus::Initializing);
277 if res.is_err() {
278 already_exists = true;
279 }
280 false
281 });
282 if already_exists {
283 return self.create_store(initial_state, _storage);
285 }
286
287 let store_handle = self.launch_store(store_id, store);
289 info!("Created store: {}", store_id);
290 store_handle
291 }
292
293 pub fn connect_to_store<T>(
294 &self,
295 store_id: OT::StoreId,
296 ) -> StoreHandle<OT, T>
298 where
299 OT::ECGHeader: Send + Sync + Clone + 'static,
300 T::Op: ConcretizeTime<<OT::ECGHeader as ECGHeader>::HeaderId>,
301 OT::ECGBody<T>: Send
302 + Serialize
303 + for<'d> Deserialize<'d>
304 + Debug
305 + ECGBody<
306 T::Op,
307 <T::Op as ConcretizeTime<<OT::ECGHeader as ECGHeader>::HeaderId>>::Serialized,
308 Header = OT::ECGHeader,
309 >,
310 <<OT as OssaType>::ECGHeader as ECGHeader>::HeaderId: Send,
314 T: CRDT<Time = OT::Time> + Clone + Debug + Send + 'static + for<'d> Deserialize<'d>,
315 {
317 let mut is_active = false;
320 self.active_stores.send_if_modified(|active_stores| {
321 let res = active_stores.try_insert(store_id, StoreStatus::Initializing);
322 if res.is_err() {
323 is_active = true;
324 }
325 false
326 });
327 if is_active {
328 return todo!();
330 }
331
332 let state = store::State::new_downloading(store_id);
336 let store_handler = self.launch_store(store_id, state);
337 debug!("Joined store: {}", store_id);
338 store_handler
339
340 }
346
347 pub fn connect() {
349 todo!("Turn on network connection")
350 }
351
352 pub fn disconnect() {
354 todo!("Turn off network connections (work offline)")
355 }
356
357 fn device_id(&self) -> DeviceId {
358 DeviceId::new(self.identity_keys.auth_key().verifying_key())
359 }
360
361 pub fn connect_to_peer_ipv4(&self, address: SocketAddrV4) {
363 let active_stores = self.active_stores.subscribe();
364 let device_id = self.device_id();
365 let shared_state = self.shared_state.clone();
366
367 let future_handle = self.tokio_runtime.spawn(async move {
369 let mut stream = match TcpStream::connect(address).await {
371 Ok(tcpstream) => {
372 let stream = codec::Framed::new(tcpstream, LengthDelimitedCodec::new());
373 TypedStream::new(stream)
374 }
375 Err(err) => {
376 todo!("TODO: Log error");
377 return;
378 }
379 };
380
381 let handshake_result = run_handshake_client(&mut stream, &device_id).await;
383 let stream = stream.finalize().into_inner();
384 debug!("Connected to server!");
385
386 let handshake_result = match handshake_result {
387 Ok(r) => r,
388 Err(HandshakeError::ConnectingToSelf) => {
389 info!("Disconnecting. Attempting to connect to ourself.");
390 return;
391 }
392 };
393
394 info!(
395 "Handshake complete with peer: {}",
396 handshake_result.peer_id()
397 );
398 if let Some(recv) = initiate_peer(handshake_result.peer_id(), &shared_state).await {
400 debug!("Start miniprotocols");
402 let args = MiniProtocolArgs::new(handshake_result.peer_id(), active_stores, recv);
403 handshake_result
404 .version()
405 .run_miniprotocols_client::<OT>(stream, args)
406 .await;
407 } else {
408 info!(
409 "Disconnecting. Already connected to peer: {}",
410 handshake_result.peer_id()
411 );
412 }
413 });
414
415 }
417
418 fn launch_store<T>(
420 &self,
421 store_id: OT::StoreId,
422 store: store::State<OT::StoreId, OT::ECGHeader, T, OT::Hash>,
423 ) -> StoreHandle<OT, T>
424 where
425 OT::ECGHeader: Send + Sync + Clone + 'static + for<'d> Deserialize<'d> + Serialize,
426 T::Op: ConcretizeTime<<OT::ECGHeader as ECGHeader>::HeaderId>,
427 OT::ECGBody<T>: Send
428 + Serialize
429 + for<'d> Deserialize<'d>
430 + Debug
431 + ECGBody<
432 T::Op,
433 <T::Op as ConcretizeTime<<OT::ECGHeader as ECGHeader>::HeaderId>>::Serialized,
434 Header = OT::ECGHeader,
435 >,
436 <<OT as OssaType>::ECGHeader as ECGHeader>::HeaderId:
439 Send + for<'d> Deserialize<'d> + Serialize,
440 T: CRDT<Time = OT::Time> + Debug + Clone + Send + 'static + for<'d> Deserialize<'d>,
442 {
443 let (send_commands, recv_commands) = tokio::sync::mpsc::unbounded_channel::<
447 store::StoreCommand<OT::ECGHeader, OT::ECGBody<T>, T>,
448 >();
449 let (send_commands_untyped, recv_commands_untyped) = tokio::sync::mpsc::unbounded_channel::<
450 store::UntypedStoreCommand<
451 OT::Hash,
452 <OT::ECGHeader as ECGHeader>::HeaderId,
453 OT::ECGHeader,
454 >,
455 >();
456
457 let shared_state = self.shared_state.clone();
462 let send_commands_untyped_ = send_commands_untyped.clone();
463 let future_handle = self.tokio_runtime.spawn(async move {
464 store::run_handler::<OT, T>(
465 store,
466 recv_commands,
467 send_commands_untyped_,
468 recv_commands_untyped,
469 shared_state,
470 )
471 .await;
472 });
473
474 self.active_stores.send_if_modified(|active_stores| {
476 let _ = active_stores.insert(
477 store_id,
478 StoreStatus::Running {
479 store_handle: future_handle,
480 send_command_chan: send_commands_untyped,
481 },
482 );
483 true
484 });
485
486 StoreHandle {
487 send_command_chan: send_commands,
489 phantom: PhantomData,
490 }
491 }
492}
493
494async fn initiate_peer<StoreId>(
496 peer_id: DeviceId,
497 shared_state: &SharedState<StoreId>,
498) -> Option<UnboundedReceiver<PeerManagerCommand<StoreId>>> {
499 let (send, recv) = tokio::sync::mpsc::unbounded_channel();
500 let inserted = {
501 let mut w = shared_state.peer_state.write().await;
502 w.try_insert(peer_id, send).is_ok()
503 };
504 if inserted {
505 Some(recv)
506 } else {
507 None
509 }
510}
511
512#[derive(Clone, Copy)]
513pub struct OssaConfig {
514 pub port: u16,
516}
517
518pub struct StoreHandle<
519 O: OssaType,
520 T: CRDT<Time = O::Time, Op: ConcretizeTime<<O::ECGHeader as ECGHeader>::HeaderId>>,
521>
522{
526 send_command_chan: UnboundedSender<StoreCommand<O::ECGHeader, O::ECGBody<T>, T>>,
528 phantom: PhantomData<O>,
529}
530
531pub trait OssaType: 'static {
533 type StoreId: Debug
534 + Display
535 + Eq
536 + Copy
537 + Ord
538 + Send
539 + Sync
540 + 'static
541 + Serialize
542 + for<'a> Deserialize<'a>
543 + AsRef<[u8]>; type Hash: util::Hash
545 + Debug
546 + Display
547 + Copy
548 + Ord
549 + Send
550 + Sync
551 + 'static
552 + Serialize
553 + for<'a> Deserialize<'a>
554 + Into<Self::StoreId>; type ECGHeader: store::ecg::ECGHeader<HeaderId: Send + Sync + Serialize + for<'a> Deserialize<'a>>
557 + Debug
558 + Send
559 + Serialize
560 + for<'a> Deserialize<'a>;
561 type ECGBody<T: CRDT<Op: ConcretizeTime<<Self::ECGHeader as ECGHeader>::HeaderId>>>; type Time;
563 type CausalState<T: CRDT<Time = Self::Time>>: CausalState<Time = Self::Time>;
565 fn to_causal_state<T: CRDT<Time = Self::Time>>(
570 st: &store::ecg::State<Self::ECGHeader, T>,
571 ) -> &Self::CausalState<T>;
572}
573
574impl<
575 O: OssaType,
576 T: CRDT<Time = O::Time, Op: ConcretizeTime<<O::ECGHeader as ECGHeader>::HeaderId>>,
577 > StoreHandle<O, T>
578{
581 pub fn apply(
582 &mut self,
583 parents: BTreeSet<<O::ECGHeader as ECGHeader>::HeaderId>,
584 op: <T::Op as ConcretizeTime<<O::ECGHeader as ECGHeader>::HeaderId>>::Serialized,
585 ) -> <O::ECGHeader as ECGHeader>::HeaderId
586 where
587 T::Op: ConcretizeTime<<O::ECGHeader as ECGHeader>::HeaderId>,
588 O::ECGBody<T>: ECGBody<
589 T::Op,
590 <T::Op as ConcretizeTime<<O::ECGHeader as ECGHeader>::HeaderId>>::Serialized,
591 Header = O::ECGHeader,
592 >,
593 {
594 self.apply_batch(parents, vec![op])
595 }
596
597 pub fn apply_batch(
599 &mut self,
600 parents: BTreeSet<<<O as OssaType>::ECGHeader as ECGHeader>::HeaderId>,
601 op: Vec<<T::Op as ConcretizeTime<<O::ECGHeader as ECGHeader>::HeaderId>>::Serialized>, ) -> <O::ECGHeader as ECGHeader>::HeaderId
604 where
605 T::Op: ConcretizeTime<<O::ECGHeader as ECGHeader>::HeaderId>,
606 <O as OssaType>::ECGBody<T>: ECGBody<
607 T::Op,
608 <T::Op as ConcretizeTime<<O::ECGHeader as ECGHeader>::HeaderId>>::Serialized,
609 Header = O::ECGHeader,
610 >,
611 {
612 let body = <<O as OssaType>::ECGBody<T> as ECGBody<
619 T::Op,
620 <T::Op as ConcretizeTime<<O::ECGHeader as ECGHeader>::HeaderId>>::Serialized,
621 >>::new_body(op);
622 let header = body.new_header(parents);
623 let header_id = header.get_header_id();
624 self.send_command_chan
627 .send(StoreCommand::Apply {
628 operation_header: header,
629 operation_body: body,
630 })
631 .expect("TODO");
632
633 header_id
635 }
636
637 pub fn subscribe_to_state(&mut self) -> UnboundedReceiver<StateUpdate<O::ECGHeader, T>> {
638 let (send_state, recv_state) = tokio::sync::mpsc::unbounded_channel();
639 self.send_command_chan
640 .send(StoreCommand::SubscribeState { send_state })
641 .expect("TODO");
642
643 recv_state
644 }
645}
646
647