Skip to main content

holochain/conductor/api/
api_cell.rs

1//! The CellConductorApi allows Cells to talk to their Conductor
2
3use super::error::ConductorApiError;
4use super::error::ConductorApiResult;
5use crate::conductor::error::ConductorResult;
6use crate::conductor::ConductorHandle;
7use crate::core::ribosome::guest_callback::post_commit::PostCommitArgs;
8use crate::core::ribosome::Ribosome;
9use crate::core::workflow::ZomeCallResult;
10use async_trait::async_trait;
11use holochain_keystore::MetaLairClient;
12use holochain_p2p::HolochainP2pResult;
13use holochain_state::conductor::WitnessNonceResult;
14use holochain_state::host_fn_workspace::SourceChainWorkspace;
15use holochain_types::prelude::*;
16use holochain_zome_types::block::Block;
17use holochain_zome_types::block::BlockTargetId;
18use std::sync::Arc;
19use tokio::sync::mpsc::error::SendError;
20use tokio::sync::mpsc::OwnedPermit;
21
22/// The concrete implementation of [`CellConductorApiT`], which is used to give
23/// Cells an API for calling back to their [`Conductor`](crate::conductor::Conductor).
24#[derive(Clone)]
25pub struct CellConductorApi {
26    conductor_handle: ConductorHandle,
27    cell_id: CellId,
28}
29
30/// Alias
31pub type CellConductorHandle = Arc<dyn CellConductorApiT + Send + 'static>;
32
33/// A minimal set of functionality needed from the conductor by
34/// host functions.
35pub type CellConductorReadHandle = Arc<dyn CellConductorReadHandleT + Send + 'static>;
36
37impl CellConductorApi {
38    /// Instantiate from a Conductor reference and a CellId to identify which Cell
39    /// this API instance is associated with
40    pub fn new(conductor_handle: ConductorHandle, cell_id: CellId) -> Self {
41        Self {
42            conductor_handle,
43            cell_id,
44        }
45    }
46}
47
48#[async_trait]
49impl CellConductorApiT for CellConductorApi {
50    fn cell_id(&self) -> &CellId {
51        &self.cell_id
52    }
53
54    fn keystore(&self) -> &MetaLairClient {
55        self.conductor_handle.keystore()
56    }
57
58    fn get_dna_def(&self, cell_id: &CellId) -> Option<DnaDef> {
59        self.conductor_handle
60            .get_dna_def(cell_id)
61            .map(|d| d.content)
62    }
63
64    fn get_this_ribosome(&self) -> ConductorApiResult<Ribosome> {
65        Ok(self.conductor_handle.get_ribosome(&self.cell_id)?)
66    }
67
68    #[cfg_attr(feature = "instrument", tracing::instrument(skip(self)))]
69    fn get_zome(&self, cell_id: &CellId, zome_name: &ZomeName) -> ConductorApiResult<Zome> {
70        let dna = self
71            .get_dna_def(cell_id)
72            .ok_or_else(|| ConductorApiError::CellMissing(cell_id.clone()))?;
73        Ok(dna.get_zome(zome_name)?)
74    }
75
76    fn get_entry_def(&self, key: &EntryDefBufferKey) -> Option<EntryDef> {
77        self.conductor_handle.get_entry_def(key)
78    }
79
80    fn into_call_zome_handle(self) -> CellConductorReadHandle {
81        Arc::new(self)
82    }
83
84    async fn post_commit_permit(&self) -> Result<OwnedPermit<PostCommitArgs>, SendError<()>> {
85        self.conductor_handle.post_commit_permit().await
86    }
87}
88
89/// The "internal" Conductor API interface, for a Cell to talk to its calling Conductor.
90#[async_trait]
91#[cfg_attr(feature = "test_utils", mockall::automock)]
92pub trait CellConductorApiT: Send + Sync {
93    /// Get this cell id
94    fn cell_id(&self) -> &CellId;
95
96    /// Request access to this conductor's keystore
97    fn keystore(&self) -> &MetaLairClient;
98
99    /// Get a [`DnaDef`] from the [`RibosomeStore`](crate::conductor::ribosome_store::RibosomeStore)
100    fn get_dna_def(&self, cell_id: &CellId) -> Option<DnaDef>;
101
102    /// Get the [`Ribosome`] of this cell from the [`RibosomeStore`](crate::conductor::ribosome_store::RibosomeStore)
103    fn get_this_ribosome(&self) -> ConductorApiResult<Ribosome>;
104
105    /// Get a [`Zome`](holochain_types::prelude::Zome) from this cell's Dna
106    fn get_zome(&self, cell_id: &CellId, zome_name: &ZomeName) -> ConductorApiResult<Zome>;
107
108    /// Get a [`EntryDef`] from the [`EntryDefBufferKey`]
109    fn get_entry_def(&self, key: &EntryDefBufferKey) -> Option<EntryDef>;
110
111    /// Turn this into a call zome handle
112    fn into_call_zome_handle(self) -> CellConductorReadHandle;
113
114    /// Get an OwnedPermit to the post commit task.
115    async fn post_commit_permit(&self) -> Result<OwnedPermit<PostCommitArgs>, SendError<()>>;
116}
117
118/// A minimal set of functionality needed from the conductor by
119/// host functions.
120#[async_trait]
121#[cfg_attr(feature = "test_utils", mockall::automock)]
122pub trait CellConductorReadHandleT: Send + Sync {
123    /// Get this cell id
124    fn cell_id(&self) -> &CellId;
125
126    /// Invoke a zome function on a Cell
127    async fn call_zome(&self, params: ZomeCallParams) -> ConductorApiResult<ZomeCallResult>;
128
129    /// Invoke a zome function on a Cell
130    async fn call_zome_with_workspace(
131        &self,
132        params: ZomeCallParams,
133        workspace_lock: SourceChainWorkspace,
134    ) -> ConductorApiResult<ZomeCallResult>;
135
136    /// Get a zome from this cell's Dna
137    fn get_zome(&self, cell_id: &CellId, zome_name: &ZomeName) -> ConductorApiResult<Zome>;
138
139    /// Get a [`EntryDef`] from the [`EntryDefBufferKey`]
140    fn get_entry_def(&self, key: &EntryDefBufferKey) -> Option<EntryDef>;
141
142    /// Try to put the nonce from a calling agent in the db. Fails with a stale result if a newer nonce exists.
143    async fn witness_nonce_from_calling_agent(
144        &self,
145        agent: AgentPubKey,
146        nonce: Nonce256Bits,
147        expires: Timestamp,
148    ) -> ConductorApiResult<WitnessNonceResult>;
149
150    /// Find the first cell ID across all apps the given cell id is in that
151    /// is assigned to the given role.
152    async fn find_cell_with_role_alongside_cell(
153        &self,
154        cell_id: &CellId,
155        role_name: &RoleName,
156    ) -> ConductorResult<Option<CellId>>;
157
158    /// Expose block functionality to zomes.
159    async fn block(&self, input: Block) -> HolochainP2pResult<()>;
160
161    /// Expose is_blocked functionality to zomes.
162    async fn is_blocked(&self, input: BlockTargetId, timestamp: Timestamp)
163        -> ConductorResult<bool>;
164
165    /// Find an installed app by one of its [CellId]s.
166    async fn find_app_containing_cell(
167        &self,
168        cell_id: &CellId,
169    ) -> ConductorResult<Option<InstalledApp>>;
170
171    /// Read the init properties supplied for this cell's role at install time.
172    async fn get_init_properties(&self) -> ConductorResult<Option<InitProperties>>;
173
174    /// Expose create_clone_cell functionality to zomes.
175    async fn create_clone_cell(
176        &self,
177        installed_app_id: &InstalledAppId,
178        payload: CreateCloneCellPayload,
179    ) -> ConductorResult<ClonedCell>;
180
181    /// Expose disable_clone_cell functionality to zomes.
182    async fn disable_clone_cell(
183        &self,
184        installed_app_id: &InstalledAppId,
185        payload: DisableCloneCellPayload,
186    ) -> ConductorResult<()>;
187
188    /// Expose enable_clone_cell functionality to zomes.
189    async fn enable_clone_cell(
190        &self,
191        installed_app_id: &InstalledAppId,
192        payload: EnableCloneCellPayload,
193    ) -> ConductorResult<ClonedCell>;
194
195    /// Expose delete_clone_cell functionality to zomes.
196    async fn delete_clone_cell(&self, payload: DeleteCloneCellPayload) -> ConductorResult<()>;
197
198    /// Accept a countersigning session.
199    #[cfg(feature = "unstable-countersigning")]
200    async fn accept_countersigning_session(
201        &self,
202        cell_id: CellId,
203        request: PreflightRequest,
204    ) -> ConductorResult<PreflightRequestAcceptance>;
205}
206
207#[async_trait]
208impl CellConductorReadHandleT for CellConductorApi {
209    fn cell_id(&self) -> &CellId {
210        &self.cell_id
211    }
212
213    async fn call_zome(&self, params: ZomeCallParams) -> ConductorApiResult<ZomeCallResult> {
214        self.conductor_handle.call_zome(params).await
215    }
216
217    async fn call_zome_with_workspace(
218        &self,
219        params: ZomeCallParams,
220        workspace_lock: SourceChainWorkspace,
221    ) -> ConductorApiResult<ZomeCallResult> {
222        if self.cell_id == params.cell_id {
223            self.conductor_handle
224                .call_zome_with_workspace(params, workspace_lock)
225                .await
226        } else {
227            self.conductor_handle.call_zome(params).await
228        }
229    }
230
231    fn get_zome(&self, cell_id: &CellId, zome_name: &ZomeName) -> ConductorApiResult<Zome> {
232        CellConductorApiT::get_zome(self, cell_id, zome_name)
233    }
234
235    fn get_entry_def(&self, key: &EntryDefBufferKey) -> Option<EntryDef> {
236        CellConductorApiT::get_entry_def(self, key)
237    }
238
239    async fn witness_nonce_from_calling_agent(
240        &self,
241        agent: AgentPubKey,
242        nonce: Nonce256Bits,
243        expires: Timestamp,
244    ) -> ConductorApiResult<WitnessNonceResult> {
245        Ok(self
246            .conductor_handle
247            .witness_nonce_from_calling_agent(agent, nonce, expires)
248            .await?)
249    }
250
251    async fn find_cell_with_role_alongside_cell(
252        &self,
253        cell_id: &CellId,
254        role_name: &RoleName,
255    ) -> ConductorResult<Option<CellId>> {
256        self.conductor_handle
257            .find_cell_with_role_alongside_cell(cell_id, role_name)
258            .await
259    }
260
261    async fn block(&self, input: Block) -> HolochainP2pResult<()> {
262        self.conductor_handle.holochain_p2p().block(input).await
263    }
264
265    async fn is_blocked(
266        &self,
267        input: BlockTargetId,
268        timestamp: Timestamp,
269    ) -> ConductorResult<bool> {
270        self.conductor_handle.is_blocked(input, timestamp).await
271    }
272
273    async fn find_app_containing_cell(
274        &self,
275        cell_id: &CellId,
276    ) -> ConductorResult<Option<InstalledApp>> {
277        self.conductor_handle
278            .find_app_containing_cell(cell_id)
279            .await
280    }
281
282    async fn get_init_properties(&self) -> ConductorResult<Option<InitProperties>> {
283        self.conductor_handle
284            .get_init_properties_for_cell(&self.cell_id)
285            .await
286    }
287
288    async fn create_clone_cell(
289        &self,
290        installed_app_id: &InstalledAppId,
291        payload: CreateCloneCellPayload,
292    ) -> ConductorResult<ClonedCell> {
293        self.conductor_handle
294            .clone()
295            .create_clone_cell(installed_app_id, payload)
296            .await
297    }
298
299    async fn disable_clone_cell(
300        &self,
301        installed_app_id: &InstalledAppId,
302        payload: DisableCloneCellPayload,
303    ) -> ConductorResult<()> {
304        self.conductor_handle
305            .clone()
306            .disable_clone_cell(installed_app_id, &payload)
307            .await
308    }
309
310    async fn enable_clone_cell(
311        &self,
312        installed_app_id: &InstalledAppId,
313        payload: EnableCloneCellPayload,
314    ) -> ConductorResult<ClonedCell> {
315        self.conductor_handle
316            .clone()
317            .enable_clone_cell(installed_app_id, &payload)
318            .await
319    }
320
321    async fn delete_clone_cell(&self, payload: DeleteCloneCellPayload) -> ConductorResult<()> {
322        self.conductor_handle
323            .clone()
324            .delete_clone_cell(&payload)
325            .await
326    }
327
328    #[cfg(feature = "unstable-countersigning")]
329    async fn accept_countersigning_session(
330        &self,
331        cell_id: CellId,
332        request: PreflightRequest,
333    ) -> ConductorResult<PreflightRequestAcceptance> {
334        self.conductor_handle
335            .accept_countersigning_session(cell_id, request)
336            .await
337    }
338}