confium_coordinator/coordinator/
client.rs1use std::io;
22use std::net::TcpStream;
23
24use crate::coordinator::net::ProtocolMessage;
25use crate::coordinator::net::recv_message;
26use crate::coordinator::net::send_message;
27use crate::coordinator::net_server::SessionIo;
28
29pub struct SignerClient {
31 stream: Box<dyn SessionIo>,
32}
33
34struct Bounded<'a> {
39 session: &'a mut Box<dyn crate::coordinator::net_server::SessionIo>,
40 deadline: std::time::Instant,
41 nonblocking: bool,
42}
43
44impl<'a> Bounded<'a> {
45 fn new(
46 session: &'a mut Box<dyn crate::coordinator::net_server::SessionIo>,
47 timeout: std::time::Duration,
48 ) -> Self {
49 let nonblocking = session.set_nonblocking(true);
50 if !nonblocking {
51 let _ = session.set_read_timeout(Some(timeout));
52 }
53 Self {
54 session,
55 deadline: std::time::Instant::now() + timeout,
56 nonblocking,
57 }
58 }
59
60 fn expired(&self) -> bool {
61 std::time::Instant::now() >= self.deadline
62 }
63}
64
65impl std::io::Read for Bounded<'_> {
66 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
67 if !self.nonblocking {
68 return self.session.read(buf);
69 }
70 loop {
71 match self.session.read(buf) {
72 Ok(n) => return Ok(n),
73 Err(ref e)
74 if e.kind() == io::ErrorKind::WouldBlock
75 || e.kind() == io::ErrorKind::TimedOut =>
76 {
77 if self.expired() {
78 return Err(io::Error::new(
79 io::ErrorKind::TimedOut,
80 "coordinator response deadline exceeded",
81 ));
82 }
83 std::thread::sleep(std::time::Duration::from_millis(2));
84 }
85 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
86 Err(e) => return Err(e),
87 }
88 }
89 }
90}
91
92impl Drop for Bounded<'_> {
93 fn drop(&mut self) {
94 if self.nonblocking {
95 self.session.set_nonblocking(false);
96 } else {
97 self.session.set_read_timeout(None);
98 }
99 }
100}
101
102impl SignerClient {
103 pub fn connect(addr: &str) -> io::Result<Self> {
105 let stream = TcpStream::connect(addr)?;
106 Ok(Self {
107 stream: Box::new(stream),
108 })
109 }
110
111 pub fn connect_url(url: &str) -> io::Result<Self> {
117 let transport = confium_net::connect(url)
118 .map_err(|e| io::Error::other(format!("connect {url}: {e}")))?;
119 Ok(Self {
120 stream: Box::new(confium_net::io::TransportIo::new(transport)),
121 })
122 }
123
124 pub fn register(&mut self, signer_id: &str, quorum_id: &str) -> io::Result<()> {
126 send_message(
127 &mut self.stream,
128 &ProtocolMessage::Register {
129 signer_id: signer_id.into(),
130 quorum_id: quorum_id.into(),
131 },
132 )?;
133 let resp = recv_message(&mut self.stream)?;
134 match resp {
135 ProtocolMessage::Registered { signer_id: sid } if sid == signer_id => Ok(()),
136 _ => Err(io::Error::new(
137 io::ErrorKind::InvalidData,
138 "unexpected register response",
139 )),
140 }
141 }
142
143 pub fn create_session(
145 &mut self,
146 quorum_id: &str,
147 scheme: &str,
148 message: &[u8],
149 threshold: u32,
150 num_parties: u32,
151 ) -> io::Result<String> {
152 send_message(
153 &mut self.stream,
154 &ProtocolMessage::CreateSession {
155 quorum_id: quorum_id.into(),
156 scheme: scheme.into(),
157 message: message.to_vec(),
158 threshold,
159 num_parties,
160 },
161 )?;
162 let resp = recv_message(&mut self.stream)?;
163 match resp {
164 ProtocolMessage::SessionCreated { session_id } => Ok(session_id),
165 ProtocolMessage::Error { message } => {
166 Err(io::Error::other(format!("coordinator error: {message}")))
167 }
168 _ => Err(io::Error::new(
169 io::ErrorKind::InvalidData,
170 "unexpected response",
171 )),
172 }
173 }
174
175 pub fn submit_commitment(
177 &mut self,
178 session_id: &str,
179 signer_id: &str,
180 commitment_bytes: &[u8],
181 ) -> io::Result<()> {
182 send_message(
183 &mut self.stream,
184 &ProtocolMessage::Commitment {
185 session_id: session_id.into(),
186 signer_id: signer_id.into(),
187 bytes: commitment_bytes.to_vec(),
188 signature: vec![0u8; 64],
189 },
190 )?;
191 let mut bounded = Bounded::new(&mut self.stream, std::time::Duration::from_secs(5));
195 match recv_message(&mut bounded) {
196 Ok(ProtocolMessage::Ack { .. }) => Ok(()),
197 Ok(ProtocolMessage::Error { message }) => {
198 Err(io::Error::other(format!("coordinator error: {message}")))
199 }
200 Ok(_) => Ok(()), Err(e) => Err(e),
202 }
203 }
204
205 pub fn submit_share(
208 &mut self,
209 session_id: &str,
210 signer_id: &str,
211 share_bytes: &[u8],
212 ) -> io::Result<Option<Vec<u8>>> {
213 send_message(
214 &mut self.stream,
215 &ProtocolMessage::Share {
216 session_id: session_id.into(),
217 signer_id: signer_id.into(),
218 bytes: share_bytes.to_vec(),
219 signature: vec![0u8; 64],
220 },
221 )?;
222
223 let mut bounded = Bounded::new(&mut self.stream, std::time::Duration::from_secs(5));
227
228 match recv_message(&mut bounded) {
229 Ok(ProtocolMessage::Signature { bytes, .. }) => Ok(Some(bytes)),
230 Ok(ProtocolMessage::Ack { .. }) => Ok(None),
231 Ok(ProtocolMessage::Error { message }) => {
232 Err(io::Error::other(format!("coordinator error: {message}")))
233 }
234 Ok(_) => Ok(None),
235 Err(ref e)
236 if e.kind() == io::ErrorKind::WouldBlock || e.kind() == io::ErrorKind::TimedOut =>
237 {
238 Ok(None)
239 }
240 Err(e) => Err(e),
241 }
242 }
243
244 pub fn get_status(&mut self, session_id: &str) -> io::Result<String> {
246 send_message(
247 &mut self.stream,
248 &ProtocolMessage::GetStatus {
249 session_id: Some(session_id.into()),
250 },
251 )?;
252 let resp = recv_message(&mut self.stream)?;
253 match resp {
254 ProtocolMessage::Status { state, .. } => Ok(state),
255 _ => Err(io::Error::new(
256 io::ErrorKind::InvalidData,
257 "unexpected status response",
258 )),
259 }
260 }
261
262 pub fn stream_mut(&mut self) -> &mut Box<dyn SessionIo> {
266 &mut self.stream
267 }
268
269 pub fn recv(&mut self) -> io::Result<ProtocolMessage> {
271 recv_message(self.stream.as_mut())
272 }
273
274 pub fn send(&mut self, msg: &ProtocolMessage) -> io::Result<()> {
276 send_message(self.stream.as_mut(), msg)
277 }
278}