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});