miden_node_store/server/
mod.rs

1use std::{path::PathBuf, sync::Arc};
2
3use miden_node_proto::generated::store::api_server;
4use miden_node_utils::{errors::ApiError, tracing::grpc::store_trace_fn};
5use tokio::net::TcpListener;
6use tokio_stream::wrappers::TcpListenerStream;
7use tower_http::trace::TraceLayer;
8use tracing::info;
9
10use crate::{
11    COMPONENT, DATABASE_MAINTENANCE_INTERVAL, GENESIS_STATE_FILENAME, blocks::BlockStore, db::Db,
12    server::db_maintenance::DbMaintenance, state::State,
13};
14
15mod api;
16mod db_maintenance;
17
18/// Represents an initialized store component where the RPC connection is open, but not yet actively
19/// responding to requests.
20///
21/// Separating the connection binding from the server spawning allows the caller to connect other
22/// components to the store without resorting to sleeps or other mechanisms to spawn dependent
23/// components.
24pub struct Store {
25    api_service: api_server::ApiServer<api::StoreApi>,
26    db_maintenance_service: DbMaintenance,
27    listener: TcpListener,
28}
29
30impl Store {
31    /// Performs initialization tasks required before [`serve`](Self::serve) can be called.
32    pub async fn init(listener: TcpListener, data_directory: PathBuf) -> Result<Self, ApiError> {
33        info!(target: COMPONENT, endpoint=?listener, ?data_directory, "Loading database");
34
35        let block_store = data_directory.join("blocks");
36        let block_store = Arc::new(BlockStore::new(block_store).await?);
37
38        let database_filepath = data_directory.join("miden-store.sqlite3");
39        let genesis_filepath = data_directory.join(GENESIS_STATE_FILENAME);
40
41        let db = Db::setup(
42            database_filepath,
43            &genesis_filepath.to_string_lossy(),
44            Arc::clone(&block_store),
45        )
46        .await
47        .map_err(|err| ApiError::ApiInitialisationFailed(err.to_string()))?;
48
49        let state = Arc::new(
50            State::load(db, block_store)
51                .await
52                .map_err(|err| ApiError::DatabaseConnectionFailed(err.to_string()))?,
53        );
54
55        let db_maintenance_service =
56            DbMaintenance::new(Arc::clone(&state), DATABASE_MAINTENANCE_INTERVAL);
57        let api_service = api_server::ApiServer::new(api::StoreApi { state });
58
59        info!(target: COMPONENT, "Database loaded");
60
61        Ok(Self {
62            api_service,
63            db_maintenance_service,
64            listener,
65        })
66    }
67
68    /// Serves the store's RPC API and DB maintenance background task.
69    ///
70    /// Note: this blocks until the server dies.
71    pub async fn serve(self) -> Result<(), ApiError> {
72        tokio::spawn(self.db_maintenance_service.run());
73        // Build the gRPC server with the API service and trace layer.
74        tonic::transport::Server::builder()
75            .layer(TraceLayer::new_for_grpc().make_span_with(store_trace_fn))
76            .add_service(self.api_service)
77            .serve_with_incoming(TcpListenerStream::new(self.listener))
78            .await
79            .map_err(ApiError::ApiServeFailed)
80    }
81}