1use anyhow::{Result, bail};
6use serde::{Deserialize, Serialize};
7use std::sync::Arc;
8
9use crate::{
10 crypto::{PeerId, Signature, SigningKey, VerifyingKey},
11 poker::{Card, Chips, PlayerCards, TableId},
12};
13
14#[derive(Debug, Serialize, Deserialize)]
16pub enum Message {
17 JoinServer {
19 nickname: String,
21 },
22 ServerJoined {
24 nickname: String,
26 chips: Chips,
28 },
29 JoinTable,
31 LeaveTable,
33 TableJoined {
35 table_id: TableId,
37 chips: Chips,
39 seats: u8,
41 },
42 NoTablesLeft,
44 NotEnoughChips,
46 PlayerAlreadyJoined,
48 PlayerJoined {
50 player_id: PeerId,
52 nickname: String,
54 chips: Chips,
56 },
57 ShowAccount {
59 chips: Chips,
61 },
62 StartGame(Vec<PeerId>),
64 StartHand,
66 EndHand {
68 payoffs: Vec<HandPayoff>,
70 board: Vec<Card>,
72 cards: Vec<(PeerId, PlayerCards)>,
74 },
75 DealCards(Card, Card),
77 PlayerLeft(PeerId),
79 GameUpdate {
81 players: Vec<PlayerUpdate>,
83 board: Vec<Card>,
85 pot: Chips,
87 },
88 ActionRequest {
90 player_id: PeerId,
92 min_raise: Chips,
94 big_blind: Chips,
96 actions: Vec<PlayerAction>,
98 },
99 ActionResponse {
101 action: PlayerAction,
103 amount: Chips,
105 },
106}
107
108#[derive(Debug, Serialize, Deserialize)]
110pub struct PlayerUpdate {
111 pub player_id: PeerId,
113 pub chips: Chips,
115 pub bet: Chips,
117 pub action: PlayerAction,
119 pub action_timer: Option<u16>,
121 pub cards: PlayerCards,
123 pub has_button: bool,
125 pub is_active: bool,
127}
128
129#[derive(Copy, Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
131pub enum PlayerAction {
132 None,
134 SmallBlind,
136 BigBlind,
138 Call,
140 Check,
142 Bet,
144 Raise,
146 Fold,
148}
149
150impl PlayerAction {
151 pub fn label(&self) -> &'static str {
153 match self {
154 PlayerAction::SmallBlind => "SB",
155 PlayerAction::BigBlind => "BB",
156 PlayerAction::Call => "CALL",
157 PlayerAction::Check => "CHECK",
158 PlayerAction::Bet => "BET",
159 PlayerAction::Raise => "RAISE",
160 PlayerAction::Fold => "FOLD",
161 PlayerAction::None => "",
162 }
163 }
164}
165
166#[derive(Clone, Debug, Serialize, Deserialize)]
168pub struct HandPayoff {
169 pub player_id: PeerId,
171 pub chips: Chips,
173 pub cards: Vec<Card>,
175 pub rank: String,
177}
178
179#[derive(Debug, Clone)]
181pub struct SignedMessage {
182 payload: Arc<Payload>,
184}
185
186#[derive(Debug, Serialize, Deserialize)]
188struct Payload {
189 msg: Message,
190 sig: Signature,
191 vk: VerifyingKey,
192}
193
194impl SignedMessage {
195 pub fn new(sk: &SigningKey, msg: Message) -> Self {
197 let sig = sk.sign(&msg);
198 Self {
199 payload: Arc::new(Payload {
200 msg,
201 sig,
202 vk: sk.verifying_key(),
203 }),
204 }
205 }
206
207 pub fn deserialize_and_verify(buf: &[u8]) -> Result<Self> {
209 let sm = Self {
210 payload: Arc::new(bincode::deserialize::<Payload>(buf)?),
211 };
212
213 if !sm.payload.vk.verify(&sm.payload.msg, &sm.payload.sig) {
214 bail!("Invalid signature");
215 }
216
217 Ok(sm)
218 }
219
220 pub fn serialize(&self) -> Vec<u8> {
222 bincode::serialize(self.payload.as_ref()).expect("Should serialize signed message")
223 }
224
225 pub fn sender(&self) -> PeerId {
227 self.payload.vk.peer_id()
228 }
229
230 pub fn message(&self) -> &Message {
232 &self.payload.msg
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239
240 #[test]
241 fn signed_message() {
242 let sk = SigningKey::default();
243 let message = Message::JoinServer {
244 nickname: "Alice".to_string(),
245 };
246
247 let smsg = SignedMessage::new(&sk, message);
248 let bytes = smsg.serialize();
249
250 let deser_msg = SignedMessage::deserialize_and_verify(&bytes).unwrap();
251 assert!(
252 matches!(deser_msg.message(), Message::JoinServer{ nickname } if nickname == "Alice")
253 );
254 }
255}