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}