ecksport_net/
channel_state.rs

1use std::collections::*;
2
3use ecksport_core::topic;
4
5use crate::errors::Error;
6
7/// Maintains the bookkeeping for our view of channels.
8#[derive(Clone, Debug)]
9pub struct ChannelTable {
10    /// Bookkeeping to assign indexes to `NewChannel` messages.
11    next_local_chan: u32,
12
13    /// Bookkeeping to assign indexes to `NewChannel` messages.
14    next_remote_chan: u32,
15
16    /// Table of channel states.
17    channels: HashMap<u32, ChannelState>,
18}
19
20impl ChannelTable {
21    /// Creates a channel table according to how the connection was initated by.
22    pub fn new(inited_by: Creator) -> Self {
23        let (nlc, nrc) = match inited_by {
24            Creator::Local => (0, 1),
25            Creator::Remote => (1, 0),
26        };
27
28        Self {
29            next_local_chan: nlc,
30            next_remote_chan: nrc,
31            channels: HashMap::new(),
32        }
33    }
34
35    /// Returns our most-recently-updated view of the number of open channels.
36    pub fn num_open_channels(&self) -> usize {
37        self.channels.len()
38    }
39
40    /// Inserts a new channel entry with the specified parameters.
41    fn init_chan(
42        &mut self,
43        creator: Creator,
44        topic: topic::Topic,
45        local_open: bool,
46        remote_open: bool,
47    ) -> u32 {
48        let id = match creator {
49            Creator::Local => self.consume_local_chan_id(),
50            Creator::Remote => self.consume_remote_chan_id(),
51        };
52
53        // Insert the channel and make sure our bookkeeping is correct.
54        let chstate = ChannelState::new(topic, local_open, remote_open);
55        assert!(self.channels.insert(id, chstate).is_none());
56
57        id
58    }
59
60    /// Inits a channel that we created.
61    pub fn init_local_chan(&mut self, topic: topic::Topic, im_close: bool) -> u32 {
62        self.init_chan(Creator::Local, topic, im_close, true)
63    }
64
65    /// Inits a channel that was created by the remote.
66    pub fn init_remote_chan(&mut self, topic: topic::Topic, im_close: bool) -> u32 {
67        self.init_chan(Creator::Remote, topic, true, im_close)
68    }
69
70    /// Marks the local side of a channel closed, if it exists.  Returns if this
71    /// cleans up the channel.
72    pub fn mark_chan_local_closed(&mut self, id: u32) -> Option<bool> {
73        let ch = self.channels.get_mut(&id)?;
74        ch.close_local();
75        let died = !ch.is_live();
76        if died {
77            self.channels.remove(&id);
78        }
79        Some(died)
80    }
81
82    /// Marks the remote side of a channel closed, if it exists.  Returns if
83    /// this cleans up the channel.
84    pub fn mark_chan_remote_closed(&mut self, id: u32) -> Option<bool> {
85        let ch = self.channels.get_mut(&id)?;
86        ch.close_remote();
87        let died = !ch.is_live();
88        if died {
89            self.channels.remove(&id);
90        }
91        Some(died)
92    }
93
94    fn consume_local_chan_id(&mut self) -> u32 {
95        let id = self.next_local_chan;
96        self.next_local_chan += 2;
97        id
98    }
99
100    fn consume_remote_chan_id(&mut self) -> u32 {
101        let id = self.next_remote_chan;
102        self.next_remote_chan += 2;
103        id
104    }
105
106    /// Checks if it's valid to receive a message on some channel ID, returning
107    /// the appropriate error if not.
108    pub fn check_recv_on_chan(&self, id: u32) -> Result<(), Error> {
109        let Some(chstate) = self.channels.get(&id) else {
110            return Err(Error::RecvOnUnkChan(id));
111        };
112
113        if !chstate.remote_open {
114            return Err(Error::RecvOnClosedChan(id));
115        }
116
117        Ok(())
118    }
119
120    /// Checks if it's valid to send a message on some channel ID, returning the
121    /// appropriate error if not.
122    pub fn check_send_on_chan(&self, id: u32) -> Result<(), Error> {
123        let Some(chstate) = self.channels.get(&id) else {
124            return Err(Error::SendOnUnkChan(id));
125        };
126
127        if !chstate.local_open {
128            return Err(Error::SendOnClosedChan(id));
129        }
130
131        Ok(())
132    }
133}
134
135/// Tracks local view of a channel's state.
136#[derive(Clone, Debug)]
137pub struct ChannelState {
138    /// Channel topic, kept here for metrics and whatnot.
139    topic: topic::Topic,
140
141    /// If the local side has yet to send a close message.
142    local_open: bool,
143
144    /// If the remote side has yet to send a close message.
145    remote_open: bool,
146}
147
148impl ChannelState {
149    pub fn new(topic: topic::Topic, local_open: bool, remote_open: bool) -> Self {
150        Self {
151            topic,
152            local_open,
153            remote_open,
154        }
155    }
156
157    pub fn topic(&self) -> topic::Topic {
158        self.topic
159    }
160
161    pub fn close_local(&mut self) {
162        self.local_open = false;
163    }
164
165    pub fn close_remote(&mut self) {
166        self.remote_open = false;
167    }
168
169    pub fn is_live(&self) -> bool {
170        self.local_open && self.remote_open
171    }
172}
173
174/// Our view of who is opening the channel.
175#[derive(Copy, Clone, Debug, Eq, PartialEq)]
176pub enum Creator {
177    Local,
178    Remote,
179}