Skip to main content

ossa_core/
core.rs

1use ossa_crdt::time::CausalState;
2// use futures::{SinkExt, StreamExt};
3// use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
4use 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 running the Ossa server.
33    thread: thread::JoinHandle<()>,
34    // command_channel: UnboundedSender<OssaCommand>,
35    tokio_runtime: Runtime,
36    /// Active stores.
37    // stores: BTreeMap<OT::StoreId,ActiveStore>,
38    active_stores: watch::Sender<
39        StoreStatuses<OT::StoreId, OT::Hash, <OT::ECGHeader as ECGHeader>::HeaderId, OT::ECGHeader>,
40    >, // JP: Make this encode more state that other's may want to subscribe to?
41    shared_state: SharedState<OT::StoreId>, // JP: Could have another thread own and manage this state
42    // instead?
43    phantom: PhantomData<OT>,
44    identity_keys: Identity,
45}
46pub type StoreStatuses<StoreId, Hash, HeaderId, Header> =
47    BTreeMap<StoreId, StoreStatus<Hash, HeaderId, Header>>; // Rename this MiniProtocolArgs?
48
49// pub enum StoreStatus<O: OssaType, T: CRDT<Time = O::Time>>
50// where
51//     T::Op: Serialize,
52pub enum StoreStatus<Hash, HeaderId, Header> {
53    // Store is initializing and async handler is being created.
54    Initializing,
55    // Store's async handler is running.
56    Running {
57        store_handle: JoinHandle<()>, // JP: Does this belong here? The state is owned here, but
58        // the miniprotocols probably don't need to block waiting on
59        // it...
60        // send_command_chan: UnboundedSender<StoreCommand<O::ECGHeader, T>>,
61        // https://www.reddit.com/r/rust/comments/1exjiab/the_amazing_pattern_i_discovered_hashmap_with/
62        // send_command_chan: UnboundedSender<StoreCommand<store::ecg::v0::Header<dyn Hash, dyn CRDT>, dyn CRDT>>,
63        // send_command_chan: UnboundedSender<UntypedStoreCommand>,
64        send_command_chan: UnboundedSender<UntypedStoreCommand<Hash, HeaderId, Header>>,
65    },
66}
67
68#[derive(Clone, Debug)]
69/// Ossa state that is shared across multiple tasks.
70pub(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    // Start ossa.
119    pub fn start(config: OssaConfig) -> Self {
120        // TODO: Load identity or take it as an argument.
121        let identity_keys = generate_identity();
122
123        // // Create channels to communicate with Ossa thread.
124        // let (send_ossa_commands, mut recv_ossa_commands) = futures_channel::mpsc::unbounded();
125        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        // Start async runtime.
133        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        // Spawn server thread.
144        let ossa_thread = thread::spawn(move || {
145            runtime_handle.block_on(async move {
146                // Start listening for connections.
147                let Some(listener) = Ossa::<OT>::bind_server_ipv4(config.port).await else {
148                    error!("Failed to start server.");
149                    return;
150                };
151
152                // // Handle commands from application.
153                // tokio::spawn(async move {
154                //     while let Some(cmd) = recv_ossa_commands.next().await {
155                //         todo!();
156                //     }
157
158                //     unreachable!();
159                // });
160
161                info!("Starting server");
162                loop {
163                    // Accept connection.
164                    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                    // Spawn async.
173                    let active_stores = active_stores_receiver.clone();
174                    // let device_id = DeviceId::new(identity_keys.auth_key().verifying_key());
175                    let shared_state = shared_state.clone();
176
177                    let future_handle = tokio::spawn(async move {
178                        // let (read_stream, write_stream) = tcpstream.split();
179                        let stream = codec::Framed::new(tcpstream, LengthDelimitedCodec::new());
180
181                        // TODO XXX
182                        // Handshake.
183                        // Diffie Hellman? TLS?
184                        // Authenticate peer's public key?
185                        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                        // Store peer in state.
202                        if let Some(recv) =
203                            initiate_peer(handshake_result.peer_id(), &shared_state).await
204                        {
205                            // Start miniprotocols.
206                            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        // TODO: Store identity key
227
228        Ossa {
229            thread: ossa_thread,
230            // command_channel: send_ossa_commands,
231            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<CausalTime<OT::Time>>: Serialize,
250        // T::Op: ConcretizeTime<T::Time>, // <OT::ECGHeader as ECGHeader>::HeaderId>,
251        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::ECGBody<T>:
263        //     Send + ECGBody<T, Header = OT::ECGHeader> + Serialize + for<'d> Deserialize<'d> + Debug,
264        <OT::ECGHeader as ECGHeader>::HeaderId: Send + Serialize + for<'d> Deserialize<'d>,
265    {
266        // Create store by generating nonce, etc.
267        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        // Check if this store id already exists and try again if there's a conflict.
273        // Otherwise, mark this store as initializing.
274        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            // This will generate a new nonce if there's a conflict.
284            return self.create_store(initial_state, _storage);
285        }
286
287        // Launch the store.
288        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        // storage: S,
297    ) -> 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        // T::Op: ConcretizeTime<T::Time>,
311        // OT::ECGBody<T>:
312        //     Send + ECGBody<T, Header = OT::ECGHeader> + Serialize + for<'d> Deserialize<'d> + Debug,
313        <<OT as OssaType>::ECGHeader as ECGHeader>::HeaderId: Send,
314        T: CRDT<Time = OT::Time> + Clone + Debug + Send + 'static + for<'d> Deserialize<'d>,
315        // T::Op<CausalTime<OT::Time>>: Serialize,
316    {
317        // Check if store is already active.
318        // If it isn't, mark it as initializing and continue.
319        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            // TODO: Get handle of existing store.
329            return todo!();
330        }
331
332        // TODO:
333        // - Load store from disk if we have it locally.
334        // Spawn async handler.
335        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        // - Add it to our active store set with the appropriate status.
341        //
342        // Update our peers + sync with them? This is automatic?
343        //
344        // TODO: Set status as initializing in create_store too
345    }
346
347    // Connect to network.
348    pub fn connect() {
349        todo!("Turn on network connection")
350    }
351
352    // Disconnect from network.
353    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    // Connect to a peer over ipv4.
362    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        // Spawn async.
368        let future_handle = self.tokio_runtime.spawn(async move {
369            // Attempt to connect to peer, returning message on failure.
370            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            // Run client handshake.
382            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            // Store peer in state.
399            if let Some(recv) = initiate_peer(handshake_result.peer_id(), &shared_state).await {
400                // Start miniprotocols.
401                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        // Return channel with peer connection status.
416    }
417
418    // TODO: Separate state (that keeps state, syncs with other peers, etc) and optional user API (that sends state updates)?
419    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::ECGBody<T>:
437        //     Send + ECGBody<T, Header = OT::ECGHeader> + Serialize + for<'d> Deserialize<'d> + Debug,
438        <<OT as OssaType>::ECGHeader as ECGHeader>::HeaderId:
439            Send + for<'d> Deserialize<'d> + Serialize,
440        // T::Op<CausalTime<OT::Time>>: Serialize,
441        T: CRDT<Time = OT::Time> + Debug + Clone + Send + 'static + for<'d> Deserialize<'d>,
442    {
443        // Initialize storage for this store.
444
445        // Create channels to handle requests and send updates.
446        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        // Add to DHT
458
459        // Spawn routine that owns this store.
460
461        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        // Register this store.
475        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            // future_handle,
488            send_command_chan: send_commands,
489            phantom: PhantomData,
490        }
491    }
492}
493
494/// Initiates a peer by creating a channel to send commands and by inserting it into the shared state. On success, returns the receiver. If the peer already exists, fails with `None`.
495async 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        // JP: Record if we're already connected to the peer?
508        None
509    }
510}
511
512#[derive(Clone, Copy)]
513pub struct OssaConfig {
514    // IPv4 port to run Ossa on.
515    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// where
523//     // T::Op: Serialize,
524//     T::Op<CausalTime<OT::Time>>: Serialize,
525{
526    // future_handle: JoinHandle<()>, // JP: Maybe this should be owned by `Ossa`?
527    send_command_chan: UnboundedSender<StoreCommand<O::ECGHeader, O::ECGBody<T>, T>>,
528    phantom: PhantomData<O>,
529}
530
531/// Trait to define newtype wrapers that instantiate type families required by Ossa.
532pub 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]>; // Hashable instead of AsRef???
544    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>; // Hashable instead of AsRef???
555                               // type ECGHeader<T: CRDT<Time = Self::Time, Op: Serialize>>: store::ecg::ECGHeader + Debug + Send;
556    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>>>; // : Serialize + for<'a> Deserialize<'a>; // : CRDT<Time = Self::Time, Op: Serialize>;
562    type Time;
563    // type CausalState<T: CRDT<Time = Self::Time, Op<CausalTime<Self::Time>>: Serialize>>: CausalState<Time = Self::Time>;
564    type CausalState<T: CRDT<Time = Self::Time>>: CausalState<Time = Self::Time>;
565    // type OperationId;
566    // type Hash: Clone + Copy + Debug + Ord + Send;
567
568    // TODO: This should be refactored and provided automatically.
569    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// where
579//     T::Op<CausalTime<T::Time>>: Serialize,
580{
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    // TODO: Don't take parents as an argument. Pull it from the state. XXX
598    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>, // T::Op<CausalTime<T::Time>>>,
602                                                                                               // op: Vec<T::Op>,
603    ) -> <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        // TODO: Divide into 256 operation chunks.
613        // if op.is_empty() {
614        //     return vec![];
615        // }
616
617        // Create ECG header and body.
618        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        // let times = body.get_operation_times(&header);
625
626        self.send_command_chan
627            .send(StoreCommand::Apply {
628                operation_header: header,
629                operation_body: body,
630            })
631            .expect("TODO");
632
633        // times
634        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// pub enum OssaCommand {
648//     CreateStore {
649//         // Since Rust doesn't have existentials...
650//         initial_state: (), // Box<Dynamic>, // T
651//         storage: Box<dyn Storage + Send>,
652//     },
653// }
654
655// fn handle_ossa_command() {
656// }