cnidarium_component/component.rs
1use std::sync::Arc;
2
3use anyhow::Result;
4use async_trait::async_trait;
5
6use cnidarium::StateWrite;
7use tendermint::abci;
8
9/// A component of a [`cnidarium`]-based application.
10///
11/// The use of `&mut Arc<S>` may seem unintuitive at first. However, it allows
12/// component implementations to optionally share state with
13/// concurrently-executing subtasks they spawn, without requiring additional
14/// locking. Components can clone the `Arc` and pass clones to concurrent,
15/// read-only subtasks, then later once the subtasks complete, use `.get_mut()`
16/// to obtain a mutable reference to the state.
17#[async_trait]
18pub trait Component {
19 /// A serialized representation of the component's application state,
20 /// passed in to [`Component::init_chain`].
21 type AppState;
22
23 /// Performs initialization, given the genesis state.
24 ///
25 /// This method is called once per chain, and should only perform
26 /// writes, since the backing tree for the [`StateWrite`] will
27 /// be empty.
28 ///
29 /// If the app is checkpointed, no genesis app state will be passed in,
30 /// and `app_state` will be `None`. Otherwise, `app_state` will be `Some`,
31 /// indicating that the chain needs to fully initialize.
32 async fn init_chain<S: StateWrite>(state: S, app_state: Option<&Self::AppState>);
33
34 /// Begins a new block, optionally inspecting the ABCI
35 /// [`BeginBlock`](abci::request::BeginBlock) request.
36 ///
37 /// # Invariants
38 ///
39 /// The `&mut Arc<S>` allows the implementor to optionally share state with
40 /// its subtasks. The implementor SHOULD assume that when the method is
41 /// called, `state.get_mut().is_some()`, i.e., the `Arc` is not shared. The
42 /// implementor MUST ensure that any clones of the `Arc` are dropped before
43 /// it returns, so that `state.get_mut().is_some()` on completion.
44 async fn begin_block<S: StateWrite + 'static>(
45 state: &mut Arc<S>,
46 begin_block: &abci::request::BeginBlock,
47 );
48
49 /// Ends the block, optionally inspecting the ABCI
50 /// [`EndBlock`](abci::request::EndBlock) request, and performing any batch
51 /// processing.
52 ///
53 /// # Invariants
54 ///
55 /// This method should only be called after [`Component::begin_block`].
56 /// No methods should be called following this method.
57 ///
58 /// The `&mut Arc<S>` allows the implementor to optionally share state with
59 /// its subtasks. The implementor SHOULD assume that when the method is
60 /// called, `state.get_mut().is_some()`, i.e., the `Arc` is not shared. The
61 /// implementor MUST ensure that any clones of the `Arc` are dropped before
62 /// it returns, so that `state.get_mut().is_some()` on completion.
63 async fn end_block<S: StateWrite + 'static>(
64 state: &mut Arc<S>,
65 end_block: &abci::request::EndBlock,
66 );
67
68 /// Ends the epoch, applying component-specific state transitions that
69 /// should occur when an epoch ends.
70 ///
71 /// Note: epochs are not an ABCI concept. They are merely logical groups of
72 /// blocks, defined by the application. Applications can choose to treat
73 /// them as a no-op.
74 ///
75 /// # Invariants
76 ///
77 /// The `&mut Arc<S>` allows the implementor to optionally share state with
78 /// its subtasks. The implementor SHOULD assume that when the method is
79 /// called, `state.get_mut().is_some()`, i.e., the `Arc` is not shared. The
80 /// implementor MUST ensure that any clones of the `Arc` are dropped before
81 /// it returns, so that `state.get_mut().is_some()` on completion.
82 async fn end_epoch<S: StateWrite + 'static>(_state: &mut Arc<S>) -> Result<()> {
83 Ok(())
84 }
85}