poi_rs/cache.rs
1// Copyright 2020-2026 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use iota_types::committee::{Committee, EpochId};
5use iota_types::digests::ChainIdentifier;
6
7use crate::BoxError;
8
9mod in_memory;
10
11pub use in_memory::MemoryCommitteeCache;
12
13/// Identifies one committee-cache entry by its network and epoch.
14///
15/// The chain identifier is the network's genesis checkpoint digest. Cache
16/// adapters must use the complete key so entries authenticated for different
17/// networks cannot collide.
18#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
19pub struct CommitteeCacheKey {
20 chain_identifier: ChainIdentifier,
21 epoch: EpochId,
22}
23
24impl CommitteeCacheKey {
25 /// Creates a cache key for `epoch` on the identified network.
26 pub const fn new(chain_identifier: ChainIdentifier, epoch: EpochId) -> Self {
27 Self {
28 chain_identifier,
29 epoch,
30 }
31 }
32
33 /// Creates a key for a cache private to one resolver.
34 pub(crate) fn isolated(epoch: EpochId) -> Self {
35 Self::new(ChainIdentifier::default(), epoch)
36 }
37
38 /// Returns the network's genesis checkpoint digest.
39 pub const fn chain_identifier(&self) -> &ChainIdentifier {
40 &self.chain_identifier
41 }
42
43 /// Returns the cached committee epoch.
44 pub const fn epoch(&self) -> EpochId {
45 self.epoch
46 }
47}
48
49/// Error returned by a committee cache.
50#[derive(Debug, thiserror::Error)]
51#[non_exhaustive]
52pub enum CommitteeCacheError {
53 /// A cached committee conflicts with authenticated committee data.
54 #[error("cached committee conflicts at epoch {epoch}")]
55 Conflict {
56 /// Epoch whose cached material conflicts.
57 epoch: EpochId,
58 },
59 /// A cache backend failed to read or write committee material.
60 #[error("committee cache backend failed at epoch {epoch}")]
61 Backend {
62 /// Epoch being accessed when the backend failed.
63 epoch: EpochId,
64 /// Underlying backend error.
65 #[source]
66 source: BoxError,
67 },
68}
69
70/// Stores authenticated committees for anchored resolution.
71///
72/// Implementations must preserve committee integrity after storage and use the
73/// complete [`CommitteeCacheKey`] for every lookup.
74#[async_trait::async_trait]
75pub trait CommitteeCache: Send + Sync {
76 /// Returns the authenticated committee for `key`, when available.
77 async fn committee(&self, key: CommitteeCacheKey) -> Result<Option<Committee>, CommitteeCacheError>;
78
79 /// Stores a committee under `key` after the resolver has authenticated it.
80 async fn store(&self, key: CommitteeCacheKey, committee: &Committee) -> Result<(), CommitteeCacheError>;
81}