use anyhow::{Result, bail};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use crate::{
crypto::{PeerId, Signature, SigningKey, VerifyingKey},
poker::{Card, Chips, PlayerCards, TableId},
};
#[derive(Debug, Serialize, Deserialize)]
pub enum Message {
JoinServer {
nickname: String,
},
ServerJoined {
nickname: String,
chips: Chips,
},
JoinTable,
LeaveTable,
TableJoined {
table_id: TableId,
chips: Chips,
seats: u8,
},
NoTablesLeft,
NotEnoughChips,
PlayerJoined {
player_id: PeerId,
nickname: String,
chips: Chips,
},
ShowAccount {
chips: Chips,
},
StartGame(Vec<PeerId>),
StartHand,
EndHand {
payoffs: Vec<HandPayoff>,
board: Vec<Card>,
cards: Vec<(PeerId, PlayerCards)>,
},
DealCards(Card, Card),
PlayerLeft(PeerId),
GameUpdate {
players: Vec<PlayerUpdate>,
board: Vec<Card>,
pot: Chips,
},
ActionRequest {
player_id: PeerId,
min_raise: Chips,
big_blind: Chips,
actions: Vec<PlayerAction>,
},
ActionResponse {
action: PlayerAction,
amount: Chips,
},
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PlayerUpdate {
pub player_id: PeerId,
pub chips: Chips,
pub bet: Chips,
pub action: PlayerAction,
pub action_timer: Option<u16>,
pub cards: PlayerCards,
pub has_button: bool,
pub is_active: bool,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub enum PlayerAction {
None,
SmallBlind,
BigBlind,
Call,
Check,
Bet,
Raise,
Fold,
}
impl PlayerAction {
pub fn label(&self) -> &'static str {
match self {
PlayerAction::SmallBlind => "SB",
PlayerAction::BigBlind => "BB",
PlayerAction::Call => "CALL",
PlayerAction::Check => "CHECK",
PlayerAction::Bet => "BET",
PlayerAction::Raise => "RAISE",
PlayerAction::Fold => "FOLD",
PlayerAction::None => "",
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct HandPayoff {
pub player_id: PeerId,
pub chips: Chips,
pub cards: Vec<Card>,
pub rank: String,
}
#[derive(Debug, Clone)]
pub struct SignedMessage {
payload: Arc<Payload>,
}
#[derive(Debug, Serialize, Deserialize)]
struct Payload {
msg: Message,
sig: Signature,
vk: VerifyingKey,
}
impl SignedMessage {
pub fn new(sk: &SigningKey, msg: Message) -> Self {
let sig = sk.sign(&msg);
Self {
payload: Arc::new(Payload {
msg,
sig,
vk: sk.verifying_key(),
}),
}
}
pub fn deserialize_and_verify(buf: &[u8]) -> Result<Self> {
let sm = Self {
payload: Arc::new(bincode::deserialize::<Payload>(buf)?),
};
if !sm.payload.vk.verify(&sm.payload.msg, &sm.payload.sig) {
bail!("Invalid signature");
}
Ok(sm)
}
pub fn serialize(&self) -> Vec<u8> {
bincode::serialize(self.payload.as_ref()).expect("Should serialize signed message")
}
pub fn sender(&self) -> PeerId {
self.payload.vk.peer_id()
}
pub fn message(&self) -> &Message {
&self.payload.msg
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn signed_message() {
let sk = SigningKey::default();
let message = Message::JoinServer {
nickname: "Alice".to_string(),
};
let smsg = SignedMessage::new(&sk, message);
let bytes = smsg.serialize();
let deser_msg = SignedMessage::deserialize_and_verify(&bytes).unwrap();
assert!(
matches!(deser_msg.message(), Message::JoinServer{ nickname } if nickname == "Alice")
);
}
}