Skip to content

Commit 18abfe6

Browse files
committed
feat: fix program issues
1 parent 77daf2d commit 18abfe6

10 files changed

Lines changed: 190 additions & 80 deletions

File tree

packages/program/programs/chess/src/constants.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ pub const MOVE_HISTORY_SEED: &[u8] = b"move_history";
55
pub const INTEGRATOR_SEED: &[u8] = b"integrator";
66
pub const TOKEN_VAULT_SEED: &[u8] = b"token_vault";
77
pub const GAME_AUTHORITY_SEED: &[u8] = b"game_authority";
8+
9+
pub const BOARD_SIZE: usize = 64;
10+
pub const MAX_MOVE_COUNT: u8 = 100;
11+
pub const MAX_TIME_CONTROL: u32 = 86400; // 24 hours

packages/program/programs/chess/src/error.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ use anchor_lang::prelude::*;
33
#[error_code]
44
pub enum ChessError {
55
#[msg("Invalid admin")]
6-
InvalidAdmin,
7-
8-
#[msg("Entry fee is too high")]
9-
EntryFeeTooHigh,
6+
InvalidAdmin,
7+
8+
#[msg("Invalid time control")]
9+
InvalidTimeControl,
10+
11+
#[msg("Entry fee is too low")]
12+
EntryFeeTooLow,
1013

1114
#[msg("Insufficient funds to create game")]
1215
InsufficientFunds,
@@ -95,6 +98,9 @@ pub enum ChessError {
9598
#[msg("Invalid game result for distribution")]
9699
InvalidGameResult,
97100

101+
#[msg("Player is already claiming winnings")]
102+
Claiming,
103+
98104
#[msg("Player has already claimed winnings")]
99105
AlreadyClaimed,
100106

@@ -118,7 +124,7 @@ pub enum ChessError {
118124

119125
#[msg("Invalid piece type")]
120126
InvalidPieceType,
121-
127+
122128
#[msg("Too many moves made in this game")]
123129
TooManyMoves,
124130
}

packages/program/programs/chess/src/instructions/accept_draw.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#[warn(ambiguous_glob_reexports)]
2+
13
use anchor_lang::prelude::*;
24
use ephemeral_rollups_sdk::{anchor::commit, ephem::commit_and_undelegate_accounts};
35

packages/program/programs/chess/src/instructions/claim_winnings.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,10 @@ pub fn handler(ctx: Context<ClaimWinnings>) -> Result<()> {
242242
ChessError::GameNotFinished
243243
);
244244

245+
let game_account = &mut ctx.accounts.game_account;
246+
require!(!game_account.is_claiming, ChessError::Claiming);
247+
game_account.is_claiming = true;
248+
245249
// Validate claimer is a game participant
246250
let claimer_color = ctx
247251
.accounts
@@ -311,6 +315,7 @@ pub fn handler(ctx: Context<ClaimWinnings>) -> Result<()> {
311315
}
312316

313317
let game_account = &ctx.accounts.game_account;
318+
314319
emit!(PlayerClaimedEvent {
315320
game_id: game_account.game_id,
316321
player: ctx.accounts.claimer.key(),
@@ -329,11 +334,7 @@ pub fn handler(ctx: Context<ClaimWinnings>) -> Result<()> {
329334
distribution_complete: game_account.distribution_complete,
330335
});
331336

332-
msg!(
333-
"Player {:?} claimed winnings: {:?}",
334-
player_color,
335-
&ctx.accounts.game_account
336-
);
337+
ctx.accounts.game_account.is_claiming = false;
337338

338339
Ok(())
339340
}

packages/program/programs/chess/src/instructions/create_game.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use anchor_spl::{
66

77
use crate::{
88
error::ChessError, transfer_from_user, ChessMove, GameAccount, IntegratorConfig, MoveHistory,
9-
TimeControl, GAME_AUTHORITY_SEED, GAME_SEED, INTEGRATOR_SEED, MOVE_HISTORY_SEED,
10-
TOKEN_VAULT_SEED,
9+
TimeControl, GAME_AUTHORITY_SEED, GAME_SEED, INTEGRATOR_SEED, MAX_MOVE_COUNT, MAX_TIME_CONTROL,
10+
MOVE_HISTORY_SEED, TOKEN_VAULT_SEED,
1111
};
1212

1313
#[derive(Accounts)]
@@ -91,11 +91,15 @@ pub struct CreateGame<'info> {
9191
}
9292

9393
impl<'info> CreateGame<'info> {
94-
pub fn validate(&self, entry_fee: u64) -> Result<()> {
95-
require!(
96-
entry_fee <= 10_000_000_000, // 10 SOL max
97-
ChessError::EntryFeeTooHigh
98-
);
94+
pub fn validate(&self, time_control: &Option<TimeControl>) -> Result<()> {
95+
// require!(entry_fee <= MAX_ENTRY_FEE, ChessError::EntryFeeTooHigh);
96+
97+
if let Some(tc) = time_control {
98+
require!(
99+
tc.initial_time <= MAX_TIME_CONTROL && tc.increment <= MAX_TIME_CONTROL,
100+
ChessError::InvalidTimeControl
101+
);
102+
}
99103

100104
Ok(())
101105
}
@@ -105,10 +109,9 @@ pub fn handler(
105109
ctx: Context<CreateGame>,
106110
entry_fee: u64,
107111
time_control: Option<TimeControl>,
108-
rated_game: bool,
109112
allow_draw_offers: bool,
110113
) -> Result<()> {
111-
ctx.accounts.validate(entry_fee)?;
114+
ctx.accounts.validate(&time_control)?;
112115

113116
let integrator_config = &mut ctx.accounts.integrator_config;
114117
let game_account = &mut ctx.accounts.game_account;
@@ -129,14 +132,13 @@ pub fn handler(
129132
ctx.accounts.token_mint.key(),
130133
ctx.accounts.token_vault.key(),
131134
time_control,
132-
rated_game,
133135
));
134136

135137
game_account.allow_draw_offers = allow_draw_offers;
136138

137139
move_history.game_account = game_account.key();
138140
move_history.history_bump = ctx.bumps.move_history;
139-
move_history.moves = [ChessMove::default(); 50];
141+
move_history.moves = [ChessMove::default(); MAX_MOVE_COUNT as usize];
140142
move_history.move_count = 0;
141143

142144
if entry_fee > 0 {

packages/program/programs/chess/src/instructions/make_move.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use ephemeral_rollups_sdk::{anchor::commit, ephem::commit_and_undelegate_account
33

44
use crate::{
55
error::ChessError, utils, ChessMove, GameAccount, GameStatus, LastMove, MoveHistory, PieceType,
6-
PlayerColor, GAME_SEED,
6+
PlayerColor, BOARD_SIZE, GAME_SEED,
77
};
88

99
#[cfg(feature = "local")]
@@ -62,8 +62,8 @@ pub struct MakeMove<'info> {
6262

6363
impl<'info> MakeMove<'info> {
6464
pub fn validate(&self, from_square: u8, to_square: u8) -> Result<()> {
65-
require!(from_square < 64, ChessError::InvalidSquare);
66-
require!(to_square < 64, ChessError::InvalidSquare);
65+
require!(from_square < (BOARD_SIZE as u8), ChessError::InvalidSquare);
66+
require!(to_square < (BOARD_SIZE as u8), ChessError::InvalidSquare);
6767
require!(from_square != to_square, ChessError::InvalidMove);
6868

6969
let game_account = &self.game_account;
@@ -79,8 +79,8 @@ impl<'info> MakeMove<'info> {
7979
PlayerColor::Black => game_account.time_used_black,
8080
};
8181

82-
let elapsed_this_move = (current_time - last_move_time) as u32;
83-
let total_time_used = time_used + elapsed_this_move;
82+
let elapsed_this_move = (current_time - last_move_time).max(0) as u32;
83+
let total_time_used = time_used.saturating_add(elapsed_this_move);
8484

8585
require!(total_time_used <= time_limit, ChessError::TimeExpired);
8686
}
@@ -160,14 +160,16 @@ pub fn handler(
160160

161161
match player_color {
162162
PlayerColor::White => {
163-
game_account.time_used_white += time_taken;
163+
game_account.time_used_white = game_account.time_used_white.saturating_add(time_taken);
164+
164165
if let Some(increment) = game_account.get_time_increment() {
165166
game_account.time_used_white =
166167
game_account.time_used_white.saturating_sub(increment);
167168
}
168169
}
169170
PlayerColor::Black => {
170-
game_account.time_used_black += time_taken;
171+
game_account.time_used_black = game_account.time_used_black.saturating_add(time_taken);
172+
171173
if let Some(increment) = game_account.get_time_increment() {
172174
game_account.time_used_black =
173175
game_account.time_used_black.saturating_sub(increment);

packages/program/programs/chess/src/instructions/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
pub mod accept_draw;
22
pub mod cancel_game;
33
pub mod claim_winnings;
4-
pub mod close_game;
54
pub mod create_game;
65
pub mod forfeit_game;
76
pub mod initialize_integrator;
@@ -15,7 +14,6 @@ pub mod update_integrator_config;
1514
pub use accept_draw::*;
1615
pub use cancel_game::*;
1716
pub use claim_winnings::*;
18-
pub use close_game::*;
1917
pub use create_game::*;
2018
pub use forfeit_game::*;
2119
pub use initialize_integrator::*;

packages/program/programs/chess/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,9 @@ pub mod chess {
6262
ctx: Context<CreateGame>,
6363
entry_fee: u64,
6464
time_control: Option<TimeControl>,
65-
rated_game: bool,
6665
allow_draw_offers: bool,
6766
) -> Result<()> {
68-
create_game::handler(ctx, entry_fee, time_control, rated_game, allow_draw_offers)
67+
create_game::handler(ctx, entry_fee, time_control, allow_draw_offers)
6968
}
7069

7170
pub fn cancel_game(ctx: Context<CancelGame>) -> Result<()> {

packages/program/programs/chess/src/state/game.rs

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use anchor_lang::prelude::*;
22

3-
use crate::error::ChessError;
3+
use crate::{error::ChessError, BOARD_SIZE, MAX_MOVE_COUNT};
44

55
#[derive(AnchorSerialize, AnchorDeserialize, Debug, InitSpace, Default, Clone, PartialEq, Eq)]
66
pub enum GameStatus {
@@ -168,15 +168,6 @@ pub struct TimeControl {
168168
pub move_time_limit: Option<u32>,
169169
}
170170

171-
#[derive(AnchorSerialize, AnchorDeserialize, Debug, InitSpace, Clone)]
172-
pub struct RatingChange {
173-
pub old_rating: u16,
174-
pub new_rating: u16,
175-
pub opponent_rating: u16,
176-
pub timestamp: i64,
177-
pub game_result: GameResult,
178-
}
179-
180171
#[account]
181172
#[derive(Debug, InitSpace)]
182173
pub struct GameAccount {
@@ -194,7 +185,7 @@ pub struct GameAccount {
194185
// Game State
195186
pub game_status: GameStatus,
196187
pub current_turn: PlayerColor,
197-
pub board_state: [u8; 64], // 8x8 board encoding
188+
pub board_state: [u8; BOARD_SIZE], // 8x8 board encoding
198189
pub move_count: u16,
199190
pub halfmove_clock: u8, // for 50-move rule
200191
pub fullmove_number: u16,
@@ -224,11 +215,11 @@ pub struct GameAccount {
224215
pub move_time_limit: Option<u32>, // seconds per move
225216
pub total_time_limit_white: Option<u32>,
226217
pub total_time_limit_black: Option<u32>,
218+
pub time_increment: Option<u32>, // seconds per move increment
227219
pub time_used_white: u32,
228220
pub time_used_black: u32,
229221

230222
// Game Settings
231-
pub rated_game: bool,
232223
pub allow_draw_offers: bool,
233224

234225
// draw
@@ -246,6 +237,8 @@ pub struct GameAccount {
246237
pub white_claimed_amount: u64,
247238
pub black_claimed_amount: u64,
248239
pub distribution_complete: bool,
240+
// state - prevent reentrancy
241+
pub is_claiming: bool,
249242
}
250243

251244
impl GameAccount {
@@ -258,7 +251,6 @@ impl GameAccount {
258251
token_mint: Pubkey,
259252
token_vault: Pubkey,
260253
time_control: Option<TimeControl>,
261-
rated_game: bool,
262254
) -> Self {
263255
let initial_board = Self::initialize_chess_board();
264256

@@ -291,12 +283,12 @@ impl GameAccount {
291283
started_at: None,
292284
last_move_at: None,
293285
finished_at: None,
294-
move_time_limit: time_control.as_ref().map(|tc| tc.move_time_limit).flatten(),
286+
move_time_limit: time_control.as_ref().and_then(|tc| tc.move_time_limit),
295287
total_time_limit_white: time_control.as_ref().map(|tc| tc.initial_time),
296288
total_time_limit_black: time_control.as_ref().map(|tc| tc.initial_time),
289+
time_increment: time_control.as_ref().map(|tc| tc.increment),
297290
time_used_white: 0,
298291
time_used_black: 0,
299-
rated_game,
300292
allow_draw_offers: true,
301293
draw_offer_pending: false,
302294
draw_offered_by: None,
@@ -311,11 +303,12 @@ impl GameAccount {
311303
white_claimed_amount: 0,
312304
black_claimed_amount: 0,
313305
distribution_complete: false,
306+
is_claiming: false,
314307
}
315308
}
316309

317-
fn initialize_chess_board() -> [u8; 64] {
318-
let mut board = [0u8; 64];
310+
fn initialize_chess_board() -> [u8; BOARD_SIZE] {
311+
let mut board = [0u8; BOARD_SIZE];
319312

320313
// White pieces (bottom ranks)
321314
board[0] = 0x14; // White Rook
@@ -356,15 +349,15 @@ impl GameAccount {
356349
}
357350

358351
pub fn get_piece_at(&self, square: u8) -> u8 {
359-
if square < 64 {
352+
if square < (BOARD_SIZE as u8) {
360353
self.board_state[square as usize]
361354
} else {
362355
0
363356
}
364357
}
365358

366359
pub fn set_piece_at(&mut self, square: u8, piece: u8) {
367-
if square < 64 {
360+
if square < (BOARD_SIZE as u8) {
368361
self.board_state[square as usize] = piece;
369362
}
370363
}
@@ -461,8 +454,7 @@ impl GameAccount {
461454

462455
/// Get time increment for this game
463456
pub fn get_time_increment(&self) -> Option<u32> {
464-
// This would be stored in time control settings if implemented
465-
None // Placeholder
457+
self.time_increment
466458
}
467459

468460
pub fn clear_draw_offer(&mut self) {
@@ -480,17 +472,18 @@ pub struct MoveHistory {
480472
pub _padding1: [u8; 3],
481473
pub move_count: u8,
482474
pub _padding2: [u8; 3],
483-
pub moves: [ChessMove; 50],
475+
pub moves: [ChessMove; MAX_MOVE_COUNT as usize],
484476
}
485477

486478
impl MoveHistory {
487479
pub fn add_move(&mut self, chess_move: ChessMove) -> Result<()> {
488-
if self.move_count >= 50 {
489-
return err!(ChessError::TooManyMoves);
490-
}
480+
require!(
481+
(self.move_count as usize) < self.moves.len(),
482+
ChessError::TooManyMoves
483+
);
491484

492485
self.moves[self.move_count as usize] = chess_move;
493-
self.move_count += 1;
486+
self.move_count = self.move_count.saturating_add(1);
494487
Ok(())
495488
}
496489
}

0 commit comments

Comments
 (0)