1use crate::GspSignal;
4use gbp::CodecError;
5use gbp_core::{BoundedSeen, GbpFlags, MemberId, SignalType, StreamType};
6use gbp_node::{GroupNode, NodeError, OutboundFrame, Sealer};
7use std::collections::HashSet;
8
9#[derive(Debug, thiserror::Error)]
11pub enum GspError {
12 #[error("decode: {0}")]
14 Decode(#[from] CodecError),
15 #[error("unknown signal_type: {0}")]
17 UnknownSignal(u32),
18 #[error("duplicate request_id: {0}")]
20 DuplicateRequest(u32),
21 #[error("node: {0}")]
23 Node(#[from] NodeError),
24}
25
26#[derive(Debug, Clone)]
29pub struct GspAccept {
30 pub signal: SignalType,
32 pub sender_id: MemberId,
34 pub role_claim: u32,
36 pub request_id: u32,
38}
39
40const GSP_SEEN_CAP: usize = 10_000;
42
43pub struct GspClient {
55 seen_requests: BoundedSeen<u32>,
56 pub muted: HashSet<MemberId>,
58 pub members: HashSet<MemberId>,
60 current_epoch: Option<u64>,
61}
62
63impl GspClient {
64 pub fn new() -> Self {
66 Self {
67 seen_requests: BoundedSeen::new(GSP_SEEN_CAP),
68 muted: HashSet::new(),
69 members: HashSet::new(),
70 current_epoch: None,
71 }
72 }
73
74 pub fn send<S: Sealer>(
76 &mut self,
77 node: &mut GroupNode,
78 seal: &mut S,
79 target: MemberId,
80 signal: SignalType,
81 role_claim: u32,
82 request_id: u32,
83 ) -> Result<OutboundFrame, GspError> {
84 self.sync_epoch(node.current_epoch);
85 let mut sig = GspSignal::bare(signal as u32, request_id, node.member_id);
86 sig.role_claim = role_claim;
87 let stream_id = node.member_stream_id(3);
88 Ok(node.send_payload(
89 seal,
90 target,
91 StreamType::Signal,
92 stream_id,
93 GbpFlags::ordered_reliable_ack(),
94 &sig.to_cbor(),
95 )?)
96 }
97
98 pub fn accept(&mut self, plaintext: &[u8], current_epoch: u64) -> Result<GspAccept, GspError> {
105 self.sync_epoch(current_epoch);
106 let s = GspSignal::from_cbor(plaintext)?;
107 let signal = SignalType::try_from(s.signal_type).map_err(GspError::UnknownSignal)?;
108 if !self.seen_requests.insert(s.request_id) {
109 return Err(GspError::DuplicateRequest(s.request_id));
110 }
111 match signal {
112 SignalType::Join => {
113 self.members.insert(s.sender_id);
114 }
115 SignalType::Leave => {
116 self.members.remove(&s.sender_id);
117 self.muted.remove(&s.sender_id);
118 }
119 SignalType::Mute => {
120 self.muted.insert(s.sender_id);
121 }
122 SignalType::Unmute => {
123 self.muted.remove(&s.sender_id);
124 }
125 _ => {}
126 }
127 Ok(GspAccept {
128 signal,
129 sender_id: s.sender_id,
130 role_claim: s.role_claim,
131 request_id: s.request_id,
132 })
133 }
134
135 pub fn sync_epoch(&mut self, epoch: u64) {
139 if Some(epoch) != self.current_epoch {
140 self.seen_requests.clear();
141 self.current_epoch = Some(epoch);
142 }
143 }
144
145 pub fn reset(&mut self) {
147 self.seen_requests.clear();
148 self.current_epoch = None;
149 }
150}