Skip to main content

bb_ops/protocols/constant_view/
mod.rs

1//! `ConstantView` — peer-selector concrete with a fixed peer list.
2//!
3//! Useful for tests + tiny fixed-size deployments where the peer
4//! set is known at install time and never changes. Implements
5//! [`bb_runtime::contracts::PeerSelector`] returning subsets of
6//! the configured list per [`SelectParams`].
7
8use bb_derive::{Concrete, PeerSelector};
9use bb_runtime::completion::{CompletionHandle, ContractResponse};
10use bb_runtime::contracts::peer_selector::SelectParams;
11use bb_runtime::ids::PeerId;
12use bb_runtime::runtime::RuntimeResourceRef;
13use serde::{Deserialize, Serialize};
14
15/// Fixed-list peer selector. Authors construct with the full
16/// peer set at compile time; queries return slices.
17#[derive(Clone, Debug, Default, Serialize, Deserialize, Concrete, PeerSelector)]
18pub struct ConstantView {
19    /// The fixed peer set, in insertion order.
20    pub peers: Vec<PeerId>,
21    /// Deterministic seed for Random selection.
22    pub seed: u64,
23}
24
25impl ConstantView {
26    /// Fresh view over `peers`. Random sampling uses a small
27    /// xorshift seeded by `seed` so the same `seed` over the same
28    /// peer set produces the same sequence — useful for tests.
29    pub fn new(peers: Vec<PeerId>, seed: u64) -> Self {
30        Self { peers, seed }
31    }
32}
33
34/// Failure modes the `ConstantView` peer selector can surface to callers.
35#[derive(Debug)]
36pub enum ConstantViewError {
37    /// The view has zero peers and `Random` / `NearKey` asked
38    /// for at least one.
39    Empty,
40}
41
42impl std::fmt::Display for ConstantViewError {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        match self {
45            Self::Empty => f.write_str("ConstantView has no peers configured"),
46        }
47    }
48}
49
50impl std::error::Error for ConstantViewError {}
51
52impl bb_runtime::contracts::PeerSelector for ConstantView {
53    type Error = ConstantViewError;
54
55    fn select(
56        &mut self,
57        _ctx: &mut RuntimeResourceRef<'_>,
58        params: SelectParams,
59        _completion: CompletionHandle<Vec<PeerId>, Self::Error>,
60    ) -> ContractResponse<Vec<PeerId>, Self::Error> {
61        match params {
62            SelectParams::All => ContractResponse::Now(Ok(self.peers.clone())),
63            SelectParams::Random { n } => {
64                if self.peers.is_empty() {
65                    return ContractResponse::Now(Err(ConstantViewError::Empty));
66                }
67                let take = (n as usize).min(self.peers.len());
68                let mut chosen: Vec<PeerId> = Vec::with_capacity(take);
69                // Deterministic xorshift seeded by `self.seed`.
70                let mut state = self.seed.wrapping_add(1);
71                while chosen.len() < take {
72                    state ^= state << 13;
73                    state ^= state >> 7;
74                    state ^= state << 17;
75                    let idx = (state as usize) % self.peers.len();
76                    let pick = self.peers[idx];
77                    if !chosen.contains(&pick) {
78                        chosen.push(pick);
79                    }
80                }
81                ContractResponse::Now(Ok(chosen))
82            }
83            SelectParams::NearKey { key: _, n } => {
84                // ConstantView doesn't model a distance metric — it
85                // just hands back the first `n` peers as a stable,
86                // deterministic answer.
87                if self.peers.is_empty() {
88                    return ContractResponse::Now(Err(ConstantViewError::Empty));
89                }
90                let take = (n as usize).min(self.peers.len());
91                ContractResponse::Now(Ok(self.peers[..take].to_vec()))
92            }
93        }
94    }
95
96    fn current_view(
97        &mut self,
98        _ctx: &mut RuntimeResourceRef<'_>,
99        _completion: CompletionHandle<Vec<PeerId>, Self::Error>,
100    ) -> ContractResponse<Vec<PeerId>, Self::Error> {
101        ContractResponse::Now(Ok(self.peers.clone()))
102    }
103}
104