Skip to main content

commonware_resolver/
lib.rs

1//! Resolve data identified by a fixed-length key.
2
3#![doc(
4    html_logo_url = "https://commonware.xyz/imgs/rustdoc_logo.svg",
5    html_favicon_url = "https://commonware.xyz/favicon.ico"
6)]
7
8commonware_macros::stability_scope!(BETA {
9    use commonware_actor::Feedback;
10    use commonware_cryptography::PublicKey;
11    use commonware_utils::{Span, channel::oneshot, vec::NonEmptyVec};
12    use core::cmp::Ordering;
13
14    pub mod delivery;
15    mod ingress;
16    pub mod opaque;
17    pub mod p2p;
18    mod subscribers;
19
20    /// A key to fetch data for a subscriber.
21    #[derive(Clone, Debug)]
22    pub struct Fetch<K, S = ()> {
23        /// The peer-visible key.
24        pub key: K,
25        /// Subscriber attached to the key.
26        pub subscriber: S,
27        /// Trace span carried from issuance to delivery.
28        pub span: tracing::Span,
29    }
30
31    impl<K: PartialEq, S: PartialEq> PartialEq for Fetch<K, S> {
32        fn eq(&self, other: &Self) -> bool {
33            self.key == other.key && self.subscriber == other.subscriber
34        }
35    }
36
37    impl<K: Eq, S: Eq> Eq for Fetch<K, S> {}
38
39    impl<K: PartialOrd, S: PartialOrd> PartialOrd for Fetch<K, S> {
40        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
41            match self.key.partial_cmp(&other.key)? {
42                Ordering::Equal => self.subscriber.partial_cmp(&other.subscriber),
43                ordering => Some(ordering),
44            }
45        }
46    }
47
48    impl<K: Ord, S: Ord> Ord for Fetch<K, S> {
49        fn cmp(&self, other: &Self) -> Ordering {
50            self.key
51                .cmp(&other.key)
52                .then_with(|| self.subscriber.cmp(&other.subscriber))
53        }
54    }
55
56    impl<K, S: Default> From<K> for Fetch<K, S> {
57        fn from(key: K) -> Self {
58            Self {
59                key,
60                subscriber: S::default(),
61                span: tracing::Span::none(),
62            }
63        }
64    }
65
66    /// Data delivered for a resolved fetch.
67    #[derive(Clone, Debug)]
68    pub struct Delivery<K, S> {
69        /// The peer-visible key used to validate the response.
70        pub key: K,
71        /// Subscribers that were still retained when the response arrived, each
72        /// paired with the trace span of the fetch that requested it.
73        pub subscribers: NonEmptyVec<(S, tracing::Span)>,
74    }
75
76    impl<K: PartialEq, S: PartialEq> PartialEq for Delivery<K, S> {
77        fn eq(&self, other: &Self) -> bool {
78            self.key == other.key
79                && self.subscribers.len() == other.subscribers.len()
80                && self
81                    .subscribers
82                    .iter()
83                    .zip(other.subscribers.iter())
84                    .all(|((a, _), (b, _))| a == b)
85        }
86    }
87
88    impl<K: Eq, S: Eq> Eq for Delivery<K, S> {}
89
90    impl<K: PartialOrd, S: PartialOrd> PartialOrd for Delivery<K, S> {
91        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
92            match self.key.partial_cmp(&other.key)? {
93                Ordering::Equal => self
94                    .subscribers
95                    .iter()
96                    .map(|(subscriber, _)| subscriber)
97                    .partial_cmp(other.subscribers.iter().map(|(subscriber, _)| subscriber)),
98                ordering => Some(ordering),
99            }
100        }
101    }
102
103    impl<K: Ord, S: Ord> Ord for Delivery<K, S> {
104        fn cmp(&self, other: &Self) -> Ordering {
105            self.key.cmp(&other.key).then_with(|| {
106                self.subscribers
107                    .iter()
108                    .map(|(subscriber, _)| subscriber)
109                    .cmp(other.subscribers.iter().map(|(subscriber, _)| subscriber))
110            })
111        }
112    }
113
114    /// Completion of the delivered subscriber snapshot.
115    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
116    pub enum Resolution {
117        /// No delivered subscribers remain unresolved.
118        Complete,
119
120        /// Some delivered subscribers remain unresolved.
121        Partial,
122    }
123
124    /// Consumer disposition for a delivered response.
125    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
126    pub enum Outcome {
127        /// The response is invalid for the peer-visible key.
128        ///
129        /// Network resolvers may penalize the serving peer before retrying.
130        Invalid,
131
132        /// The response is valid for the peer-visible key and resolves at least one delivered subscriber.
133        ///
134        /// [`Resolution::Complete`] retires the delivered subscribers. [`Resolution::Partial`]
135        /// retries the key. Network resolvers may credit the serving peer in either case.
136        Resolved(Resolution),
137
138        /// The response is valid for the peer-visible key but resolves no delivered subscribers.
139        ///
140        /// The resolver retries without crediting or penalizing the serving peer.
141        Unresolved,
142
143        /// The consumer no longer needs the key, so the response does not need to be validated.
144        ///
145        /// The resolver retires the key and all of its subscribers without retrying or
146        /// attributing the response to its source.
147        Ignored,
148    }
149
150    impl From<bool> for Outcome {
151        fn from(valid: bool) -> Self {
152            if valid {
153                Self::Resolved(Resolution::Complete)
154            } else {
155                Self::Invalid
156            }
157        }
158    }
159
160    /// Determines the disposition of data returned for a fetch.
161    pub trait Consumer: Clone + Send + 'static {
162        /// Type used to key data requested from peers.
163        type Key: Span;
164
165        /// Type of data to retrieve.
166        type Value;
167
168        /// Type used to track subscribers on fetch keys.
169        type Subscriber: Clone + Eq + Send + 'static;
170
171        /// Delivery disposition returned after validation.
172        ///
173        /// Consumers that only distinguish valid and invalid data may use
174        /// `bool`, which maps `true` to [`Resolved(Resolution::Complete)`](crate::Outcome::Resolved)
175        /// and `false` to [`Invalid`](crate::Outcome::Invalid).
176        type Outcome: Into<crate::Outcome> + Send + 'static;
177
178        /// Deliver data to the consumer.
179        ///
180        /// Returns a receiver that reports whether the response completes the
181        /// delivery, is invalid, is valid but leaves subscribers unresolved, or
182        /// can be ignored because the key is no longer needed.
183        ///
184        /// Removing the last subscriber cancels the delivery. A verdict sent before the
185        /// receiver closes still contributes source feedback, but cannot complete or
186        /// retry a later fetch for the same key.
187        ///
188        /// If all delivered subscribers were already satisfied without this response,
189        /// drop the verdict sender. Dropping the sender retires only the delivered
190        /// subscribers, without crediting or penalizing the source. Any remaining
191        /// subscribers receive the same response.
192        ///
193        /// Implementations of [`Resolver`] must only invoke `deliver` for keys that were
194        /// previously requested via [`Resolver::fetch`] (or [`TargetedResolver`] variants).
195        ///
196        /// `delivery` contains the peer-visible key and the retained subscribers
197        /// for the fetch. Subscribers decide who should observe a valid response;
198        /// they do not define peer validity.
199        fn deliver(
200            &mut self,
201            delivery: Delivery<Self::Key, Self::Subscriber>,
202            value: Self::Value,
203        ) -> oneshot::Receiver<Self::Outcome>;
204    }
205
206    /// Responsible for fetching data and notifying a `Consumer`.
207    pub trait Resolver: Clone + Send + 'static {
208        /// Type used to key data requested from peers.
209        type Key: Span;
210
211        /// Type used to track subscribers on fetch keys.
212        ///
213        /// Implementations that also own the [`Consumer`] should supply subscribers to
214        /// [`Consumer::deliver`] when a fetch resolves.
215        type Subscriber: Clone + Eq + Send + 'static;
216
217        /// Initiate a fetch.
218        ///
219        /// The resolver fetches and delivers the key. The subscriber is
220        /// retained and supplied to [`Consumer::deliver`] when the fetch resolves.
221        /// If multiple subscribers are attached to the same key,
222        /// the fetch is retained as long as at least one subscriber satisfies the
223        /// latest [`retain`](Self::retain) predicate.
224        ///
225        /// Passing a bare key is supported when `Subscriber: Default`.
226        fn fetch<F>(&mut self, key: F) -> Feedback
227        where
228            F: Into<Fetch<Self::Key, Self::Subscriber>> + Send;
229
230        /// Initiate fetches for a batch of keys.
231        fn fetch_all<F>(&mut self, keys: Vec<F>) -> Feedback
232        where
233            F: Into<Fetch<Self::Key, Self::Subscriber>> + Send;
234
235        /// Retain only fetch subscribers satisfying the predicate.
236        ///
237        /// The predicate receives the peer-visible key and subscriber.
238        ///
239        /// Fetches not retained are canceled. If response validation is in
240        /// progress, cancellation may drop the [`Consumer::deliver`] future
241        /// before it reports whether the data was valid.
242        fn retain(
243            &mut self,
244            predicate: impl Fn(&Self::Key, &Self::Subscriber) -> bool + Send + 'static,
245        ) -> Feedback;
246    }
247
248    /// Extension for resolvers that accept target peer hints.
249    pub trait TargetedResolver: Resolver {
250        /// Type used to identify peers for targeted fetch hints.
251        type PublicKey: PublicKey;
252
253        /// Initiate a fetch with target peer hints.
254        ///
255        /// Implementations define whether target hints persist through retries,
256        /// merge with existing in-progress fetches, or are discarded.
257        fn fetch_targeted(
258            &mut self,
259            fetch: impl Into<Fetch<Self::Key, Self::Subscriber>> + Send,
260            targets: NonEmptyVec<Self::PublicKey>,
261        ) -> Feedback;
262
263        /// Initiate fetches for multiple keys, each with their own target hints.
264        ///
265        /// See [`fetch_targeted`](Self::fetch_targeted) for details on target behavior.
266        fn fetch_all_targeted<F>(
267            &mut self,
268            keys: Vec<(F, NonEmptyVec<Self::PublicKey>)>,
269        ) -> Feedback
270        where
271            F: Into<Fetch<Self::Key, Self::Subscriber>> + Send;
272    }
273});