hdi/
op.rs

1//! Helper types for working with [`Op`]s
2use crate::prelude::*;
3
4#[cfg(test)]
5mod test;
6
7/// This trait provides a conversion to a convenience type [`FlatOp`]
8/// for use in the validation call back.
9///
10/// Not all data is available in the [`FlatOp`]. This is why the [`Op`]
11/// is not taken by value and can still be used after this conversion.
12///
13/// There is data that is common to all ops and can be accessed via helpers on
14/// the op.
15/// - Get the [`Op::author()`] of the op.
16/// - Get the [`Op::timestamp()`] for when the op was created.
17/// - Get the [`Op::action_seq()`] of the op.
18/// - Get the [`Op::prev_action()`] of the op.
19/// - Get the [`Op::action_type()`] of the op.
20pub trait OpHelper {
21    /// Converts an [`Op`] to a [`FlatOp`] without consuming it.
22    /// This will clone the required internal data.
23    fn flattened<ET, LT>(&self) -> Result<FlatOp<ET, LT>, WasmError>
24    where
25        ET: EntryTypesHelper + UnitEnum,
26        <ET as UnitEnum>::Unit: Into<ZomeEntryTypesKey>,
27        LT: LinkTypesHelper,
28        WasmError: From<<ET as EntryTypesHelper>::Error>,
29        WasmError: From<<LT as LinkTypesHelper>::Error>;
30}
31
32/// All possible variants that an [`RegisterAgentActivity`]
33/// with an [`Action`] that has an [`EntryType`] can produce.
34#[derive(Debug)]
35enum ActivityEntry<Unit> {
36    App { entry_type: Option<Unit> },
37    PrivateApp { entry_type: Option<Unit> },
38    Agent(AgentPubKey),
39    CapClaim(EntryHash),
40    CapGrant(EntryHash),
41}
42
43impl OpHelper for Op {
44    fn flattened<ET, LT>(&self) -> Result<FlatOp<ET, LT>, WasmError>
45    where
46        ET: EntryTypesHelper + UnitEnum,
47        <ET as UnitEnum>::Unit: Into<ZomeEntryTypesKey>,
48        LT: LinkTypesHelper,
49        WasmError: From<<ET as EntryTypesHelper>::Error>,
50        WasmError: From<<LT as LinkTypesHelper>::Error>,
51    {
52        match self {
53            Op::StoreRecord(StoreRecord { record }) => {
54                let r = match record.action() {
55                    Action::Dna(action) => OpRecord::Dna {
56                        dna_hash: action.hash.clone(),
57                        action: action.clone(),
58                    },
59                    Action::AgentValidationPkg(action) => {
60                        let AgentValidationPkg { membrane_proof, .. } = action;
61                        OpRecord::AgentValidationPkg {
62                            membrane_proof: membrane_proof.clone(),
63                            action: action.clone(),
64                        }
65                    }
66                    Action::InitZomesComplete(action) => OpRecord::InitZomesComplete {
67                        action: action.clone(),
68                    },
69                    Action::CreateLink(action) => {
70                        let CreateLink {
71                            zome_index,
72                            link_type,
73                            base_address,
74                            target_address,
75                            tag,
76                            ..
77                        } = action;
78                        let link_type = in_scope_link_type(*zome_index, *link_type)?;
79                        OpRecord::CreateLink {
80                            base_address: base_address.clone(),
81                            target_address: target_address.clone(),
82                            tag: tag.clone(),
83                            link_type,
84                            action: action.clone(),
85                        }
86                    }
87                    Action::DeleteLink(action) => {
88                        let DeleteLink {
89                            base_address,
90                            link_add_address,
91                            ..
92                        } = action;
93                        OpRecord::DeleteLink {
94                            original_action_hash: link_add_address.clone(),
95                            base_address: base_address.clone(),
96                            action: action.clone(),
97                        }
98                    }
99                    Action::OpenChain(action) => {
100                        let OpenChain {
101                            prev_target,
102                            close_hash,
103                            ..
104                        } = action;
105                        OpRecord::OpenChain {
106                            previous_target: prev_target.clone(),
107                            close_hash: close_hash.clone(),
108                            action: action.clone(),
109                        }
110                    }
111                    Action::CloseChain(action) => {
112                        let CloseChain { new_target, .. } = action;
113                        OpRecord::CloseChain {
114                            new_target: new_target.clone(),
115                            action: action.clone(),
116                        }
117                    }
118                    Action::Create(action) => {
119                        let Create {
120                            entry_type,
121                            entry_hash,
122                            ..
123                        } = action;
124                        match entry_type {
125                            EntryType::AgentPubKey => OpRecord::CreateAgent {
126                                agent: entry_hash.clone().into(),
127                                action: action.clone(),
128                            },
129                            EntryType::App(entry_def) => {
130                                match get_app_entry_type_for_record_authority(
131                                    entry_def,
132                                    record.entry.as_option(),
133                                )? {
134                                    UnitEnumEither::Enum(app_entry) => OpRecord::CreateEntry {
135                                        app_entry,
136                                        action: action.clone(),
137                                    },
138                                    UnitEnumEither::Unit(app_entry_type) => {
139                                        OpRecord::CreatePrivateEntry {
140                                            app_entry_type,
141                                            action: action.clone(),
142                                        }
143                                    }
144                                }
145                            }
146                            EntryType::CapClaim => OpRecord::CreateCapClaim {
147                                action: action.clone(),
148                            },
149                            EntryType::CapGrant => OpRecord::CreateCapGrant {
150                                action: action.clone(),
151                            },
152                        }
153                    }
154                    Action::Update(action) => {
155                        let Update {
156                            entry_type,
157                            entry_hash,
158                            original_action_address: original_action_hash,
159                            original_entry_address: original_entry_hash,
160                            ..
161                        } = action;
162                        match entry_type {
163                            EntryType::AgentPubKey => OpRecord::UpdateAgent {
164                                original_key: original_entry_hash.clone().into(),
165                                original_action_hash: original_action_hash.clone(),
166                                new_key: entry_hash.clone().into(),
167                                action: action.clone(),
168                            },
169                            EntryType::App(entry_def) => {
170                                match get_app_entry_type_for_record_authority(
171                                    entry_def,
172                                    record.entry.as_option(),
173                                )? {
174                                    UnitEnumEither::Enum(app_entry) => OpRecord::UpdateEntry {
175                                        original_action_hash: original_action_hash.clone(),
176                                        original_entry_hash: original_entry_hash.clone(),
177                                        app_entry,
178                                        action: action.clone(),
179                                    },
180                                    UnitEnumEither::Unit(app_entry_type) => {
181                                        OpRecord::UpdatePrivateEntry {
182                                            original_action_hash: original_action_hash.clone(),
183                                            original_entry_hash: original_entry_hash.clone(),
184                                            app_entry_type,
185                                            action: action.clone(),
186                                        }
187                                    }
188                                }
189                            }
190                            EntryType::CapClaim => OpRecord::UpdateCapClaim {
191                                original_action_hash: original_action_hash.clone(),
192                                original_entry_hash: original_entry_hash.clone(),
193                                action: action.clone(),
194                            },
195                            EntryType::CapGrant => OpRecord::UpdateCapGrant {
196                                original_action_hash: original_action_hash.clone(),
197                                original_entry_hash: original_entry_hash.clone(),
198                                action: action.clone(),
199                            },
200                        }
201                    }
202                    Action::Delete(action) => {
203                        let Delete {
204                            deletes_address,
205                            deletes_entry_address,
206                            ..
207                        } = action;
208                        OpRecord::DeleteEntry {
209                            original_action_hash: deletes_address.clone(),
210                            original_entry_hash: deletes_entry_address.clone(),
211                            action: action.clone(),
212                        }
213                    }
214                };
215                Ok(FlatOp::StoreRecord(r))
216            }
217            Op::StoreEntry(StoreEntry { action, entry }) => {
218                let r = match &action.hashed.content {
219                    EntryCreationAction::Create(action) => {
220                        let Create {
221                            entry_type,
222                            entry_hash,
223                            ..
224                        } = action;
225                        match entry_type {
226                            EntryType::AgentPubKey => OpEntry::CreateAgent {
227                                agent: entry_hash.clone().into(),
228                                action: action.clone(),
229                            },
230                            EntryType::App(app_entry) => OpEntry::CreateEntry {
231                                app_entry: get_app_entry_type_for_store_entry_authority(app_entry, entry)?,
232                                action: action.clone(),
233                            },
234                            EntryType::CapClaim => OpEntry::CreateCapClaim {
235                                entry: match entry {
236                                    Entry::CapClaim(entry) => entry.clone(),
237                                    _ => return Err(wasm_error!(WasmErrorInner::Guest(format!("Entry type does not match. CapClaim expected but got: {entry:?}"))))
238                                },
239                                action: action.clone(),
240                            },
241                            EntryType::CapGrant => OpEntry::CreateCapGrant {
242                                entry: match entry {
243                                    Entry::CapGrant(entry) => entry.clone(),
244                                    _ => return Err(wasm_error!(WasmErrorInner::Guest(format!("Entry type does not match. CapGrant expected but got: {entry:?}"))))
245                                },
246                                action: action.clone(),
247                            },
248                        }
249                    }
250                    EntryCreationAction::Update(action) => {
251                        let Update {
252                            original_action_address: original_action_hash,
253                            original_entry_address: original_entry_hash,
254                            entry_type,
255                            entry_hash,
256                            ..
257                        } = action;
258                        match entry_type {
259                            EntryType::AgentPubKey => OpEntry::UpdateAgent {
260                                original_key: original_entry_hash.clone().into(),
261                                original_action_hash: original_action_hash.clone(),
262                                new_key: entry_hash.clone().into(),
263                                action: action.clone(),
264                            },
265                            EntryType::App(entry_def) => {
266                                let app_entry = get_app_entry_type_for_store_entry_authority(entry_def, entry)?;
267                                OpEntry::UpdateEntry {
268                                    original_action_hash: original_action_hash.clone(),
269                                    original_entry_hash: original_entry_hash.clone(),
270                                    app_entry,
271                                    action: action.clone(),
272                                }
273                            }
274                            EntryType::CapClaim => OpEntry::UpdateCapClaim {
275                                original_action_hash: original_action_hash.clone(),
276                                original_entry_hash: original_entry_hash.clone(),
277                                entry: match entry {
278                                    Entry::CapClaim(entry) => entry.clone(),
279                                    _ => return Err(wasm_error!(WasmErrorInner::Guest(format!("Entry type does not match. CapClaim expected but got: {entry:?}"))))
280                                },
281                                action: action.clone(),
282                            },
283                            EntryType::CapGrant => OpEntry::UpdateCapGrant {
284                                original_action_hash: original_action_hash.clone(),
285                                original_entry_hash: original_entry_hash.clone(),
286                                entry: match entry {
287                                    Entry::CapGrant(entry) => entry.clone(),
288                                    _ => return Err(wasm_error!(WasmErrorInner::Guest(format!("Entry type does not match. CapGrant expected but got: {entry:?}"))))
289                                },
290                                action: action.clone(),
291                            },
292                        }
293                    }
294                };
295                Ok(FlatOp::StoreEntry(r))
296            }
297            Op::RegisterUpdate(RegisterUpdate { update, new_entry }) => {
298                let Update {
299                    original_action_address: original_action_hash,
300                    original_entry_address: original_entry_hash,
301                    entry_type,
302                    entry_hash,
303                    ..
304                } = &update.hashed.content;
305                let update = match entry_type {
306                    EntryType::AgentPubKey => OpUpdate::Agent {
307                        original_key: original_entry_hash.clone().into(),
308                        original_action_hash: original_action_hash.clone(),
309                        new_key: entry_hash.clone().into(),
310                        action: update.hashed.content.clone(),
311                    },
312                    EntryType::App(entry_def) => {
313                        let new_entry = get_app_entry_type_for_record_authority::<ET>(
314                            entry_def,
315                            new_entry.as_ref(),
316                        )?;
317                        match new_entry {
318                            UnitEnumEither::Enum(new) => OpUpdate::Entry {
319                                app_entry: new,
320                                action: update.hashed.content.clone(),
321                            },
322                            UnitEnumEither::Unit(new) => OpUpdate::PrivateEntry {
323                                original_action_hash: original_action_hash.clone(),
324                                app_entry_type: new,
325                                action: update.hashed.content.clone(),
326                            },
327                        }
328                    }
329                    EntryType::CapClaim => OpUpdate::CapClaim {
330                        original_action_hash: original_action_hash.clone(),
331                        action: update.hashed.content.clone(),
332                    },
333                    EntryType::CapGrant => OpUpdate::CapGrant {
334                        original_action_hash: original_action_hash.clone(),
335                        action: update.hashed.content.clone(),
336                    },
337                };
338                Ok(FlatOp::RegisterUpdate(update))
339            }
340            Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
341                let r = match &action.hashed.content {
342                    Action::Dna(action) => {
343                        let Dna { hash, .. } = action;
344                        OpActivity::Dna {
345                            dna_hash: hash.clone(),
346                            action: action.clone(),
347                        }
348                    }
349                    Action::AgentValidationPkg(action) => {
350                        let AgentValidationPkg { membrane_proof, .. } = action;
351                        OpActivity::AgentValidationPkg {
352                            membrane_proof: membrane_proof.clone(),
353                            action: action.clone(),
354                        }
355                    }
356                    Action::InitZomesComplete(action) => OpActivity::InitZomesComplete {
357                        action: action.clone(),
358                    },
359                    Action::OpenChain(action) => {
360                        let OpenChain {
361                            prev_target,
362                            close_hash,
363                            ..
364                        } = action;
365                        OpActivity::OpenChain {
366                            previous_target: prev_target.clone(),
367                            close_hash: close_hash.clone(),
368                            action: action.clone(),
369                        }
370                    }
371                    Action::CloseChain(action) => {
372                        let CloseChain { new_target, .. } = action;
373                        OpActivity::CloseChain {
374                            new_target: new_target.clone(),
375                            action: action.clone(),
376                        }
377                    }
378                    Action::CreateLink(action) => {
379                        let CreateLink {
380                            base_address,
381                            target_address,
382                            zome_index,
383                            link_type,
384                            tag,
385                            ..
386                        } = action;
387                        let link_type = activity_link_type(*zome_index, *link_type)?;
388                        OpActivity::CreateLink {
389                            base_address: base_address.clone(),
390                            target_address: target_address.clone(),
391                            tag: tag.clone(),
392                            link_type,
393                            action: action.clone(),
394                        }
395                    }
396                    Action::DeleteLink(action) => {
397                        let DeleteLink {
398                            link_add_address,
399                            base_address,
400                            ..
401                        } = action;
402                        OpActivity::DeleteLink {
403                            original_action_hash: link_add_address.clone(),
404                            base_address: base_address.clone(),
405                            action: action.clone(),
406                        }
407                    }
408                    Action::Create(action) => {
409                        let Create {
410                            entry_type,
411                            entry_hash,
412                            ..
413                        } = action;
414                        match activity_entry::<ET>(entry_type, entry_hash)? {
415                            ActivityEntry::App { entry_type, .. } => OpActivity::CreateEntry {
416                                app_entry_type: entry_type,
417                                action: action.clone(),
418                            },
419                            ActivityEntry::PrivateApp { entry_type, .. } => {
420                                OpActivity::CreatePrivateEntry {
421                                    app_entry_type: entry_type,
422                                    action: action.clone(),
423                                }
424                            }
425                            ActivityEntry::Agent(agent) => OpActivity::CreateAgent {
426                                agent,
427                                action: action.clone(),
428                            },
429                            ActivityEntry::CapClaim(_hash) => OpActivity::CreateCapClaim {
430                                action: action.clone(),
431                            },
432                            ActivityEntry::CapGrant(_hash) => OpActivity::CreateCapGrant {
433                                action: action.clone(),
434                            },
435                        }
436                    }
437                    Action::Update(action) => {
438                        let Update {
439                            original_action_address,
440                            original_entry_address,
441                            entry_type,
442                            entry_hash,
443                            ..
444                        } = action;
445                        match activity_entry::<ET>(entry_type, entry_hash)? {
446                            ActivityEntry::App { entry_type, .. } => OpActivity::UpdateEntry {
447                                original_action_hash: original_action_address.clone(),
448                                original_entry_hash: original_entry_address.clone(),
449                                app_entry_type: entry_type,
450                                action: action.clone(),
451                            },
452                            ActivityEntry::PrivateApp { entry_type, .. } => {
453                                OpActivity::UpdatePrivateEntry {
454                                    original_action_hash: original_action_address.clone(),
455                                    original_entry_hash: original_entry_address.clone(),
456                                    app_entry_type: entry_type,
457                                    action: action.clone(),
458                                }
459                            }
460                            ActivityEntry::Agent(new_key) => OpActivity::UpdateAgent {
461                                original_action_hash: original_action_address.clone(),
462                                original_key: original_entry_address.clone().into(),
463                                new_key,
464                                action: action.clone(),
465                            },
466                            ActivityEntry::CapClaim(_entry_hash) => OpActivity::UpdateCapClaim {
467                                original_action_hash: original_action_address.clone(),
468                                original_entry_hash: original_entry_address.clone(),
469                                action: action.clone(),
470                            },
471                            ActivityEntry::CapGrant(_entry_hash) => OpActivity::UpdateCapGrant {
472                                original_action_hash: original_action_address.clone(),
473                                original_entry_hash: original_entry_address.clone(),
474                                action: action.clone(),
475                            },
476                        }
477                    }
478                    Action::Delete(action) => {
479                        let Delete {
480                            deletes_address,
481                            deletes_entry_address,
482                            ..
483                        } = action;
484                        OpActivity::DeleteEntry {
485                            original_action_hash: deletes_address.clone(),
486                            original_entry_hash: deletes_entry_address.clone(),
487                            action: action.clone(),
488                        }
489                    }
490                };
491                Ok(FlatOp::RegisterAgentActivity(r))
492            }
493            Op::RegisterCreateLink(RegisterCreateLink { create_link }) => {
494                let CreateLink {
495                    base_address,
496                    target_address,
497                    zome_index,
498                    link_type,
499                    tag,
500                    ..
501                } = &create_link.hashed.content;
502                let link_type = in_scope_link_type(*zome_index, *link_type)?;
503                Ok(FlatOp::RegisterCreateLink {
504                    base_address: base_address.clone(),
505                    target_address: target_address.clone(),
506                    tag: tag.clone(),
507                    link_type,
508                    action: create_link.hashed.content.clone(),
509                })
510            }
511            Op::RegisterDeleteLink(RegisterDeleteLink {
512                delete_link,
513                create_link,
514            }) => {
515                let CreateLink {
516                    base_address,
517                    target_address,
518                    zome_index,
519                    link_type,
520                    tag,
521                    ..
522                } = create_link;
523                let link_type = in_scope_link_type(*zome_index, *link_type)?;
524                Ok(FlatOp::RegisterDeleteLink {
525                    original_action: create_link.clone(),
526                    base_address: base_address.clone(),
527                    target_address: target_address.clone(),
528                    tag: tag.clone(),
529                    link_type,
530                    action: delete_link.hashed.content.clone(),
531                })
532            }
533            Op::RegisterDelete(RegisterDelete { delete }) => Ok(FlatOp::RegisterDelete(OpDelete {
534                action: delete.hashed.content.clone(),
535            })),
536        }
537    }
538}
539
540/// Produces the user-defined entry type enum. Even if the entry is private, this will succeed.
541/// To be used only in the context of a StoreEntry authority.
542fn get_app_entry_type_for_store_entry_authority<ET>(
543    entry_def: &AppEntryDef,
544    entry: &Entry,
545) -> Result<ET, WasmError>
546where
547    ET: EntryTypesHelper + UnitEnum,
548    <ET as UnitEnum>::Unit: Into<ZomeEntryTypesKey>,
549    WasmError: From<<ET as EntryTypesHelper>::Error>,
550{
551    let entry_type = <ET as EntryTypesHelper>::deserialize_from_type(
552        entry_def.zome_index,
553        entry_def.entry_index,
554        entry,
555    )?;
556    match entry_type {
557        Some(entry_type) => Ok(entry_type),
558        None => Err(deny_other_zome()),
559    }
560}
561
562/// Produces the user-defined entry type enum or the unit enum if entry is not present.
563/// To be used only in the context of a StoreRecord or AgentActivity authority.
564/// If the entry's availability does not match the defined visibility, an error will result.
565fn get_app_entry_type_for_record_authority<ET>(
566    entry_def: &AppEntryDef,
567    entry: Option<&Entry>,
568) -> Result<UnitEnumEither<ET>, WasmError>
569where
570    ET: EntryTypesHelper + UnitEnum,
571    <ET as UnitEnum>::Unit: Into<ZomeEntryTypesKey>,
572    WasmError: From<<ET as EntryTypesHelper>::Error>,
573{
574    let AppEntryDef {
575        zome_index,
576        entry_index: entry_def_index,
577        visibility,
578        ..
579    } = entry_def;
580    match (entry, visibility) {
581        (Some(entry), EntryVisibility::Public) => {
582            get_app_entry_type_for_store_entry_authority(entry_def, entry).map(UnitEnumEither::Enum)
583        }
584
585        (None, EntryVisibility::Private) => {
586            match get_unit_entry_type::<ET>(*zome_index, *entry_def_index)? {
587                Some(unit) => Ok(UnitEnumEither::Unit(unit)),
588                None => Err(deny_other_zome()),
589            }
590        }
591
592        (Some(_), EntryVisibility::Private) => Err(wasm_error!(WasmErrorInner::Guest(format!(
593            "Entry visibility is private but an entry was provided! entry_def: {entry_def:?}"
594        )))),
595
596        (None, EntryVisibility::Public) => Err(wasm_error!(WasmErrorInner::Guest(format!(
597            "Entry visibility is public but no entry is available. entry_def: {entry_def:?}"
598        )))),
599    }
600}
601
602/// Maps [`RegisterAgentActivity`] ops to their
603/// entries. The entry type will be [`None`] if
604/// the zome id is not a dependency of this zome.
605fn activity_entry<ET>(
606    entry_type: &EntryType,
607    entry_hash: &EntryHash,
608) -> Result<ActivityEntry<<ET as UnitEnum>::Unit>, WasmError>
609where
610    ET: UnitEnum,
611    <ET as UnitEnum>::Unit: Into<ZomeEntryTypesKey>,
612{
613    match entry_type {
614        EntryType::App(AppEntryDef {
615            zome_index,
616            entry_index: entry_def_index,
617            visibility,
618        }) => {
619            let unit = get_unit_entry_type::<ET>(*zome_index, *entry_def_index)?;
620            match visibility {
621                EntryVisibility::Public => Ok(ActivityEntry::App { entry_type: unit }),
622                EntryVisibility::Private => Ok(ActivityEntry::PrivateApp { entry_type: unit }),
623            }
624        }
625        EntryType::AgentPubKey => Ok(ActivityEntry::Agent(entry_hash.clone().into())),
626        EntryType::CapClaim => Ok(ActivityEntry::CapClaim(entry_hash.clone())),
627        EntryType::CapGrant => Ok(ActivityEntry::CapGrant(entry_hash.clone())),
628    }
629}
630
631/// Get the app defined link type from a [`ZomeIndex`] and [`LinkType`].
632/// If the [`ZomeIndex`] is not a dependency of this zome then return a host error.
633fn in_scope_link_type<LT>(zome_index: ZomeIndex, link_type: LinkType) -> Result<LT, WasmError>
634where
635    LT: LinkTypesHelper,
636    WasmError: From<<LT as LinkTypesHelper>::Error>,
637{
638    match <LT as LinkTypesHelper>::from_type(*zome_index, *link_type)? {
639        Some(link_type) => Ok(link_type),
640        None => Err(deny_other_zome()),
641    }
642}
643
644/// Get the app defined link type from a [`ZomeIndex`] and [`LinkType`].
645/// If the [`ZomeIndex`] is not a dependency of this zome then return a host error.
646fn activity_link_type<LT>(
647    zome_index: ZomeIndex,
648    link_type: LinkType,
649) -> Result<Option<LT>, WasmError>
650where
651    LT: LinkTypesHelper,
652    WasmError: From<<LT as LinkTypesHelper>::Error>,
653{
654    Ok(<LT as LinkTypesHelper>::from_type(*zome_index, *link_type)?)
655}
656
657/// Produce the unit variant given a zome id and entry def index.
658/// Returns [`None`] if the zome id is not a dependency of this zome.
659/// Returns a [`WasmErrorInner::Guest`] error if the zome id is a
660/// dependency but the [`EntryDefIndex`] is out of range.
661fn get_unit_entry_type<ET>(
662    zome_index: ZomeIndex,
663    entry_def_index: EntryDefIndex,
664) -> Result<Option<<ET as UnitEnum>::Unit>, WasmError>
665where
666    ET: UnitEnum,
667    <ET as UnitEnum>::Unit: Into<ZomeEntryTypesKey>,
668{
669    let entries = zome_info()?.zome_types.entries;
670    let unit = entries.find(
671        <ET as UnitEnum>::unit_iter(),
672        ScopedEntryDefIndex {
673            zome_index,
674            zome_type: entry_def_index,
675        },
676    );
677    let unit = match unit {
678        Some(unit) => Some(unit),
679        None => {
680            if entries.dependencies().any(|z| z == zome_index) {
681                return Err(wasm_error!(WasmErrorInner::Guest(format!(
682                    "Entry type: {entry_def_index:?} is out of range for this zome."
683                ))));
684            } else {
685                None
686            }
687        }
688    };
689    Ok(unit)
690}
691
692/// Produce an error because this zome
693/// should never be called with a zome id
694/// that is not a dependency.
695fn deny_other_zome() -> WasmError {
696    wasm_error!(WasmErrorInner::Host(
697        "Op called for zome it was not defined in. This is a Holochain bug".to_string()
698    ))
699}