Skip to main content

canic_core/ops/runtime/
root_funding.rs

1//! Module: ops::runtime::root_funding
2//!
3//! Responsibility: bridge the sole core cycle-top-up owner to Root funding authority.
4//! Does not own: funding policy, durable journals, Coordinator calls, or timer scheduling.
5//! Boundary: a Root control plane registers one authority-owning driver before runtime start.
6
7use crate::{
8    InternalError,
9    dto::fleet_funding::{FleetRootFundingRequest, FleetRootFundingResponse},
10};
11use async_trait::async_trait;
12use std::sync::OnceLock;
13
14/// Immutable scheduling inputs selected from one Root's protected funding authority.
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16pub struct RootFundingRuntimeConfig {
17    pub request_threshold: u128,
18    pub cooldown_secs: u64,
19}
20
21/// Driver implemented by the Root control plane while core retains timer ownership.
22#[async_trait]
23pub trait RootFundingRuntime: Send + Sync {
24    /// Return the protected normal funding schedule.
25    fn config(&self) -> Result<RootFundingRuntimeConfig, InternalError>;
26
27    /// Validate and return the exact durable nonterminal request, if one exists.
28    fn current_request(&self) -> Result<Option<FleetRootFundingRequest>, InternalError>;
29
30    /// Persist the next request before its first outbound call.
31    fn prepare_request(&self) -> Result<FleetRootFundingRequest, InternalError>;
32
33    /// Invoke the exact Coordinator request without changing Root journal state.
34    async fn request(
35        &self,
36        request: FleetRootFundingRequest,
37    ) -> Result<FleetRootFundingResponse, InternalError>;
38
39    /// Commit one exact terminal Coordinator response.
40    fn record_response(
41        &self,
42        response: FleetRootFundingResponse,
43    ) -> Result<FleetRootFundingResponse, InternalError>;
44}
45
46static ROOT_FUNDING_RUNTIME: OnceLock<&'static dyn RootFundingRuntime> = OnceLock::new();
47
48/// Process-local Root funding driver registry used by the cycle workflow.
49pub struct RootFundingRuntimeApi;
50
51impl RootFundingRuntimeApi {
52    /// Register the control-plane driver before Root lifecycle restoration.
53    pub fn register(runtime: &'static dyn RootFundingRuntime) {
54        let _ = ROOT_FUNDING_RUNTIME.set(runtime);
55    }
56
57    pub(crate) fn config() -> Result<RootFundingRuntimeConfig, InternalError> {
58        runtime()?.config()
59    }
60
61    pub(crate) fn current_request() -> Result<Option<FleetRootFundingRequest>, InternalError> {
62        runtime()?.current_request()
63    }
64
65    pub(crate) fn prepare_request() -> Result<FleetRootFundingRequest, InternalError> {
66        runtime()?.prepare_request()
67    }
68
69    pub(crate) async fn request(
70        request: FleetRootFundingRequest,
71    ) -> Result<FleetRootFundingResponse, InternalError> {
72        runtime()?.request(request).await
73    }
74
75    pub(crate) fn record_response(
76        response: FleetRootFundingResponse,
77    ) -> Result<FleetRootFundingResponse, InternalError> {
78        runtime()?.record_response(response)
79    }
80}
81
82fn runtime() -> Result<&'static dyn RootFundingRuntime, InternalError> {
83    ROOT_FUNDING_RUNTIME
84        .get()
85        .copied()
86        .ok_or_else(InternalError::lifecycle_failure)
87}