From 5b378e106e5389ca08538fbee89296c297fdfe76 Mon Sep 17 00:00:00 2001 From: Steve Degosserie Date: Mon, 30 Oct 2023 20:57:39 +0100 Subject: [PATCH] Start refactoring to move game execution off-chain. --- src/lib.rs | 364 +++++---------------- src/tests.rs | 892 +++++++++++++++++---------------------------------- 2 files changed, 381 insertions(+), 875 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f0fef33..103fee8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,16 +61,10 @@ pub mod pallet { Daily, // 1 day } - #[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq)] - pub enum NextMove { - Whites, - Blacks, - } - #[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq)] pub enum MatchState { AwaitingOpponent, - OnGoing(NextMove), + OnGoing, Won, Drawn, } @@ -80,13 +74,9 @@ pub mod pallet { pub struct Match { pub challenger: T::AccountId, pub opponent: T::AccountId, - // no need for BoundedVec, we only modify this internally (and safely) - // introducing an extra runtime constant would be overkill - pub board: Vec, pub state: MatchState, pub nonce: u128, pub style: MatchStyle, - pub last_move: BlockNumberFor, pub start: BlockNumberFor, pub bet_asset_id: AssetIdOf, pub bet_amount: T::AssetBalance, @@ -277,7 +267,6 @@ pub mod pallet { MatchCreated(T::AccountId, T::AccountId, T::Hash), MatchAborted(T::Hash), MatchStarted(T::Hash), - MoveExecuted(T::Hash, T::AccountId, Vec), MatchWon(T::Hash, T::AccountId, Vec), MatchDrawn(T::Hash, Vec), MatchRefundError(T::Hash), @@ -293,12 +282,9 @@ pub mod pallet { NotMatchOpponent, NotMatchChallenger, InvalidBoardEncoding, - InvalidMoveEncoding, NotAwaitingOpponent, StillAwaitingOpponent, MatchAlreadyFinished, - NotYourTurn, - IllegalMove, BetTooLow, BetDoesNotExist, MatchNotOnGoing, @@ -306,8 +292,6 @@ pub mod pallet { MoveNotExpired, } - const MOVE_FEN_LENGTH: usize = 4; - type GenesisInfo = (AccountIdOf, u16); #[pallet::genesis_config] @@ -353,11 +337,9 @@ pub mod pallet { let new_match: Match = Match { challenger: challenger.clone(), opponent: opponent.clone(), - board: Self::init_board(), state: MatchState::AwaitingOpponent, nonce: nonce.clone(), style, - last_move: 0u32.into(), start: 0u32.into(), bet_asset_id, bet_amount, @@ -424,7 +406,7 @@ pub mod pallet { chess_match.opponent_bet()?; - chess_match.state = MatchState::OnGoing(NextMove::Whites); + chess_match.state = MatchState::OnGoing; chess_match.start = >::block_number(); >::insert(match_id, chess_match); @@ -433,202 +415,89 @@ pub mod pallet { Ok(()) } - #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::make_move())] - pub fn make_move( - origin: OriginFor, - match_id: T::Hash, - move_fen: Vec, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - ensure!( - move_fen.len() == MOVE_FEN_LENGTH, - Error::::InvalidMoveEncoding - ); - - let mut chess_match: Match = match Self::chess_matches(match_id) { - Some(m) => m, - None => return Err(Error::::NonExistentMatch.into()), - }; - - match chess_match.state { - MatchState::AwaitingOpponent => { - return Err(Error::::StillAwaitingOpponent.into()) - } - MatchState::Won | MatchState::Drawn => { - return Err(Error::::MatchAlreadyFinished.into()) - } - MatchState::OnGoing(NextMove::Whites) => { - if who != chess_match.challenger { - return Err(Error::::NotYourTurn.into()); - } - } - MatchState::OnGoing(NextMove::Blacks) => { - if who != chess_match.opponent { - return Err(Error::::NotYourTurn.into()); - } - } - } - - let mut board_obj: Board = Self::decode_board(chess_match.board)?; - let move_obj: Move = Self::decode_move(move_fen.clone())?; - - if !board_obj.is_legal(move_obj) { - return Err(Error::::IllegalMove.into()); - } - - // we already checked for legality, so we call play_unchecked (faster) - board_obj.play_unchecked(move_obj); - - // check game status: Won? Drawn? OnGoing? - chess_match.state = match board_obj.status() { - GameStatus::Ongoing => match board_obj.side_to_move() { - Color::White => MatchState::OnGoing(NextMove::Whites), - Color::Black => MatchState::OnGoing(NextMove::Blacks), - }, - GameStatus::Won => MatchState::Won, - GameStatus::Drawn => MatchState::Drawn, - }; - - chess_match.board = Self::encode_board(board_obj); - chess_match.last_move = >::block_number(); - - Self::deposit_event(Event::MoveExecuted(match_id, who.clone(), move_fen)); - if chess_match.state == MatchState::Won { - Self::deposit_event(Event::MatchWon( - match_id, - who.clone(), - chess_match.board.clone(), - )); - - // winner gets both deposits - chess_match.win_bet(&who)?; - - // update elo rating - let (score_1, score_2) = if chess_match.challenger == who { - (1_f32, 0_f32) - } else { - (0_f32, 1_f32) - }; - Self::update_elo( - chess_match.challenger.clone(), - score_1, - chess_match.opponent.clone(), - score_2, - ); - - // match is over, clean up storage - >::remove(match_id); - >::remove(chess_match.challenger.clone(), match_id); - >::remove(chess_match.opponent, match_id); - >::remove(chess_match.nonce); - } else if chess_match.state == MatchState::Drawn { - Self::deposit_event(Event::MatchDrawn(match_id, chess_match.board.clone())); - - // return deposit to both players - chess_match.refund_bets()?; - - // update elo rating - Self::update_elo( - chess_match.challenger.clone(), - 0.5, - chess_match.opponent.clone(), - 0.5, - ); - - // match is over, clean up storage - >::remove(match_id); - >::remove(chess_match.challenger.clone(), match_id); - >::remove(chess_match.opponent, match_id); - >::remove(chess_match.nonce); - } else { - // match still ongoing, update on-chain board - >::insert(match_id, chess_match); - } - - Ok(()) - } - - #[pallet::call_index(4)] - #[pallet::weight(T::WeightInfo::clear_abandoned_match())] - pub fn clear_abandoned_match(origin: OriginFor, match_id: T::Hash) -> DispatchResult { - let who = ensure_signed(origin)?; - let chess_match = match Self::chess_matches(match_id) { - Some(m) => m, - None => return Err(Error::::NonExistentMatch.into()), - }; - - ensure!( - (chess_match.state == MatchState::OnGoing(NextMove::Whites)) - | (chess_match.state == MatchState::OnGoing(NextMove::Blacks)), - Error::::MatchNotOnGoing - ); - - let now = >::block_number(); - let diff = now - chess_match.last_move; - - let expired: bool = match chess_match.style { - MatchStyle::Bullet => diff > T::BulletPeriod::get(), - MatchStyle::Blitz => diff > T::BlitzPeriod::get(), - MatchStyle::Rapid => diff > T::RapidPeriod::get(), - MatchStyle::Daily => diff > T::DailyPeriod::get(), - }; - - ensure!(expired, Error::::MoveNotExpired); - - let winner = match chess_match.state { - MatchState::OnGoing(NextMove::Whites) => chess_match.opponent.clone(), - _ => chess_match.challenger.clone(), - }; - - Self::deposit_event(Event::MatchWon( - match_id, - winner.clone(), - chess_match.board.clone(), - )); - - let abandoned: bool = match chess_match.style { - MatchStyle::Bullet => diff > T::BulletPeriod::get() * 10u32.into(), - MatchStyle::Blitz => diff > T::BlitzPeriod::get() * 10u32.into(), - MatchStyle::Rapid => diff > T::RapidPeriod::get() * 10u32.into(), - MatchStyle::Daily => diff > T::DailyPeriod::get() * 10u32.into(), - }; - - if (who == chess_match.challenger) | (who == chess_match.opponent) | !abandoned { - // winner gets both deposits before match becomes abandoned - match chess_match.win_bet(&winner) { - Ok(()) => {} - Err(_) => Self::deposit_event(Event::MatchAwardError(match_id, winner.clone())), - } - } else { - // who cleared the match after match is abandoned gets the incentive, - // and the winner gets both deposits minus the incentive share - match chess_match.clear_abandoned_bet(&winner, &who) { - Ok(()) => {} - Err(_) => Self::deposit_event(Event::MatchClearanceError( - match_id, - winner.clone(), - who, - )), - } - } - - // update elo rating - let looser = if chess_match.challenger == winner { - chess_match.opponent.clone() - } else { - chess_match.challenger.clone() - }; - Self::update_elo(winner.clone(), 1_f32, looser, 0_f32); - - >::remove(match_id); - >::remove(chess_match.challenger.clone(), match_id); - >::remove(chess_match.opponent, match_id); - >::remove(chess_match.nonce); - - Ok(()) - } + // TODO: to be refactored. Implements a dispute resolution mechanism whereby players + // can submit their latest play along with the zkVM execution proof. The play that will + // be deemed the latest (as per the fullmove counter) and verified, will determine who + // is the winner. + // #[pallet::call_index(4)] + // #[pallet::weight(T::WeightInfo::clear_abandoned_match())] + // pub fn clear_abandoned_match(origin: OriginFor, match_id: T::Hash) -> DispatchResult { + // let who = ensure_signed(origin)?; + // let chess_match = match Self::chess_matches(match_id) { + // Some(m) => m, + // None => return Err(Error::::NonExistentMatch.into()), + // }; + + // ensure!( + // (chess_match.state == MatchState::OnGoing(NextMove::Whites)) + // | (chess_match.state == MatchState::OnGoing(NextMove::Blacks)), + // Error::::MatchNotOnGoing + // ); + + // let now = >::block_number(); + // let diff = now - chess_match.last_move; + + // let expired: bool = match chess_match.style { + // MatchStyle::Bullet => diff > T::BulletPeriod::get(), + // MatchStyle::Blitz => diff > T::BlitzPeriod::get(), + // MatchStyle::Rapid => diff > T::RapidPeriod::get(), + // MatchStyle::Daily => diff > T::DailyPeriod::get(), + // }; + + // ensure!(expired, Error::::MoveNotExpired); + + // let winner = match chess_match.state { + // MatchState::OnGoing(NextMove::Whites) => chess_match.opponent.clone(), + // _ => chess_match.challenger.clone(), + // }; + + // Self::deposit_event(Event::MatchWon( + // match_id, + // winner.clone(), + // chess_match.board.clone(), + // )); + + // let abandoned: bool = match chess_match.style { + // MatchStyle::Bullet => diff > T::BulletPeriod::get() * 10u32.into(), + // MatchStyle::Blitz => diff > T::BlitzPeriod::get() * 10u32.into(), + // MatchStyle::Rapid => diff > T::RapidPeriod::get() * 10u32.into(), + // MatchStyle::Daily => diff > T::DailyPeriod::get() * 10u32.into(), + // }; + + // if (who == chess_match.challenger) | (who == chess_match.opponent) | !abandoned { + // // winner gets both deposits before match becomes abandoned + // match chess_match.win_bet(&winner) { + // Ok(()) => {} + // Err(_) => Self::deposit_event(Event::MatchAwardError(match_id, winner.clone())), + // } + // } else { + // // who cleared the match after match is abandoned gets the incentive, + // // and the winner gets both deposits minus the incentive share + // match chess_match.clear_abandoned_bet(&winner, &who) { + // Ok(()) => {} + // Err(_) => Self::deposit_event(Event::MatchClearanceError( + // match_id, + // winner.clone(), + // who, + // )), + // } + // } + + // // update elo rating + // let looser = if chess_match.challenger == winner { + // chess_match.opponent.clone() + // } else { + // chess_match.challenger.clone() + // }; + // Self::update_elo(winner.clone(), 1_f32, looser, 0_f32); + + // >::remove(match_id); + // >::remove(chess_match.challenger.clone(), match_id); + // >::remove(chess_match.opponent, match_id); + // >::remove(chess_match.nonce); + + // Ok(()) + // } } impl Pallet { @@ -645,14 +514,7 @@ pub mod pallet { T::Hashing::hash_of(&(challenger, opponent, nonce)) } - fn init_board() -> Vec { - format!("{}", Board::default()).as_bytes().to_vec() - } - - fn encode_board(board: Board) -> Vec { - format!("{}", board).as_bytes().to_vec() - } - + fn decode_board(encoded_board: Vec) -> sp_std::result::Result> { let s = match from_utf8(encoded_board.as_slice()) { Ok(s) => s, @@ -664,60 +526,6 @@ pub mod pallet { } } - fn decode_move(encoded_move: Vec) -> sp_std::result::Result> { - let s = match from_utf8(encoded_move.as_slice()) { - Ok(s) => s, - Err(_) => "", - }; - match Move::from_str(s) { - Ok(m) => Ok(m), - Err(_) => Err(Error::::InvalidMoveEncoding.into()), - } - } - - // needed for benchmarking - // todo: check if pub is a vulnerability - pub fn force_board_state( - match_id: T::Hash, - encoded_board: Vec, - ) -> sp_std::result::Result<(), Error> { - let mut chess_match = match Self::chess_matches(match_id) { - Some(m) => m, - None => return Err(Error::::NonExistentMatch.into()), - }; - - chess_match.board = encoded_board.clone(); - - let board_obj = Self::decode_board(encoded_board)?; - chess_match.state = match board_obj.status() { - GameStatus::Ongoing => match board_obj.side_to_move() { - Color::White => MatchState::OnGoing(NextMove::Whites), - Color::Black => MatchState::OnGoing(NextMove::Blacks), - }, - GameStatus::Won => MatchState::Won, - GameStatus::Drawn => MatchState::Drawn, - }; - - if chess_match.state == MatchState::Won { - // match is over, clean up storage - >::remove(match_id); - >::remove(chess_match.challenger.clone(), match_id); - >::remove(chess_match.opponent, match_id); - >::remove(chess_match.nonce); - } else if chess_match.state == MatchState::Drawn { - // match is over, clean up storage - >::remove(match_id); - >::remove(chess_match.challenger.clone(), match_id); - >::remove(chess_match.opponent, match_id); - >::remove(chess_match.nonce); - } else { - // match still ongoing, update on-chain board - >::insert(match_id, chess_match); - } - - Ok(()) - } - fn update_elo(player1: T::AccountId, score_1: f32, player2: T::AccountId, score_2: f32) { let rating1 = Self::player_elo(&player1) as f32; let rating2 = Self::player_elo(&player2) as f32; diff --git a/src/tests.rs b/src/tests.rs index e6d74eb..34ba62a 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,5 +1,4 @@ -use crate::{mock::*, Config, Error, Event, MatchState, MatchStyle, NextMove, PlayerMatches}; -use cozy_chess::Board; +use crate::{mock::*, Config, Error, MatchState, MatchStyle, PlayerMatches}; use frame_benchmarking::account; use frame_support::{assert_noop, assert_ok}; @@ -67,10 +66,10 @@ fn create_match_works() { assert_eq!(chess_match.challenger, alice); assert_eq!(chess_match.opponent, bob); - assert_eq!( - chess_match.board, - Board::default().to_string().as_bytes().to_vec() - ); + // assert_eq!( + // chess_match.board, + // Board::default().to_string().as_bytes().to_vec() + // ); assert_eq!(chess_match.state, MatchState::AwaitingOpponent); assert_eq!(chess_match.nonce, 0); @@ -141,7 +140,7 @@ fn join_match_works() { assert_ok!(Chess::join_match(RuntimeOrigin::signed(bob), match_id)); let chess_match = Chess::chess_matches(match_id).unwrap(); - assert_eq!(chess_match.state, MatchState::OnGoing(NextMove::Whites)); + assert_eq!(chess_match.state, MatchState::OnGoing); let final_balance_a = Assets::balance(bet_asset_id, alice); let final_balance_b = Assets::balance(bet_asset_id, bob); @@ -150,592 +149,291 @@ fn join_match_works() { }); } -#[test] -fn make_move_works() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - - let alice = account("Alice", 0, 0); - let bob = account("Bob", 0, 1); - - let bet_asset_id = AssetId::get(); - - let initial_balance_a = Assets::balance(bet_asset_id, alice); - let initial_balance_b = Assets::balance(bet_asset_id, bob); - - let bet_amount = AssetMinBalance::get() * 5; // assuming T::IncentiveShare is 10% - - assert_ok!(Chess::create_match( - RuntimeOrigin::signed(alice), - bob, - MatchStyle::Bullet, - bet_asset_id, - bet_amount - )); - - let match_id = Chess::chess_match_id_from_nonce(0).unwrap(); - - assert_ok!(Chess::join_match(RuntimeOrigin::signed(bob), match_id)); - - // test successful make_move - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "e2e4".into() - )); - System::assert_last_event( - Event::MoveExecuted { - 0: match_id, - 1: alice, - 2: "e2e4".into(), - } - .into(), - ); - - // test NotYourTurn error - assert_noop!( - Chess::make_move(RuntimeOrigin::signed(alice), match_id, "e7e5".into()), - Error::::NotYourTurn - ); - - // test IllegalMove error - assert_noop!( - Chess::make_move(RuntimeOrigin::signed(bob), match_id, "e2e4".into()), - Error::::IllegalMove - ); - - // test InvalidMoveEncoding - assert_noop!( - Chess::make_move(RuntimeOrigin::signed(bob), match_id, "1234".into()), - Error::::InvalidMoveEncoding - ); - - // test InvalidMoveEncoding - assert_noop!( - Chess::make_move(RuntimeOrigin::signed(bob), match_id, "e1e2e3".into()), - Error::::InvalidMoveEncoding - ); - - // test InvalidMoveEncoding - assert_noop!( - Chess::make_move(RuntimeOrigin::signed(bob), match_id, "1".into()), - Error::::InvalidMoveEncoding - ); - - // test MatchWon - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "e7e5".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "g1f3".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "b8c6".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "d2d4".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "e5d4".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "f3d4".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "f8c5".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "c2c3".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "d8f6".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "d4c6".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "f6f2".into() - )); - System::assert_has_event( - Event::MatchWon { - 0: match_id, - 1: bob, - 2: "r1b1k1nr/pppp1ppp/2N5/2b5/4P3/2P5/PP3qPP/RNBQKB1R w KQkq - 0 7".into(), - } - .into(), - ); - assert_eq!(Chess::chess_matches(match_id), None); - - let final_balance_a = Assets::balance(bet_asset_id, alice); - let final_balance_b = Assets::balance(bet_asset_id, bob); - assert_eq!(final_balance_a, initial_balance_a - bet_amount); - assert_eq!(final_balance_b, initial_balance_b + bet_amount); - - // -------------------------------------- - // test MatchDrawn - let initial_balance_a = Assets::balance(bet_asset_id, alice); - let initial_balance_b = Assets::balance(bet_asset_id, bob); - - assert_ok!(Chess::create_match( - RuntimeOrigin::signed(alice), - bob, - MatchStyle::Bullet, - bet_asset_id, - bet_amount - )); - - let match_id = Chess::chess_match_id_from_nonce(1).unwrap(); - - assert_ok!(Chess::join_match(RuntimeOrigin::signed(bob), match_id)); - - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "c2c4".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "h7h5".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "h2h4".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "a7a5".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "d1a4".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "a8a6".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "a4a5".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "a6h6".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "a5c7".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "f7f6".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "c7d7".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "e8f7".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "d7b7".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "d8d3".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "b7b8".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "d3h7".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "b8c8".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "f7g6".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "c8e6".into() - )); - System::assert_has_event( - Event::MatchDrawn { - 0: match_id, - 1: "5bnr/4p1pq/4Qpkr/7p/2P4P/8/PP1PPPP1/RNB1KBNR b KQ - 2 10".into(), - } - .into(), - ); - assert_eq!(Chess::chess_matches(match_id), None); - - let final_balance_a = Assets::balance(bet_asset_id, alice); - let final_balance_b = Assets::balance(bet_asset_id, bob); - assert_eq!(final_balance_a, initial_balance_a); - assert_eq!(final_balance_b, initial_balance_b); - }); -} - -#[test] -fn check_elo_stronger_wins() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - - let alice = account("Alice", 0, 0); - let bob = account("Bob", 0, 1); - - let bet_asset_id = AssetId::get(); - let bet_amount = AssetMinBalance::get() * 5; - - assert_ok!(Chess::create_match( - RuntimeOrigin::signed(alice), - bob, - MatchStyle::Bullet, - bet_asset_id, - bet_amount - )); - - let match_id = Chess::chess_match_id_from_nonce(0).unwrap(); - - assert_ok!(Chess::join_match(RuntimeOrigin::signed(bob), match_id)); - - // check elo before the match finishes - assert_eq!(Chess::player_elo(alice), 2000); - assert_eq!(Chess::player_elo(bob), 2400); - - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "f2f3".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "e7e5".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "g2g4".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "d8h4".into() - )); - - assert_eq!(Chess::chess_matches(match_id), None); - - // check the elo after match is complete - assert_eq!(Chess::player_elo(alice), 1997); - assert_eq!(Chess::player_elo(bob), 2403); - }); -} - -#[test] -fn check_elo_stronger_looses() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - - let alice = account("Alice", 0, 0); - let bob = account("Bob", 0, 1); - - let bet_asset_id = AssetId::get(); - let bet_amount = AssetMinBalance::get() * 5; - - assert_ok!(Chess::create_match( - RuntimeOrigin::signed(bob), - alice, - MatchStyle::Bullet, - bet_asset_id, - bet_amount - )); - - let match_id = Chess::chess_match_id_from_nonce(0).unwrap(); - - assert_ok!(Chess::join_match(RuntimeOrigin::signed(alice), match_id)); - - // check elo before the match finishes - assert_eq!(Chess::player_elo(alice), 2000); - assert_eq!(Chess::player_elo(bob), 2400); - - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "f2f3".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "e7e5".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "g2g4".into() - )); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "d8h4".into() - )); - - assert_eq!(Chess::chess_matches(match_id), None); - - // check the elo after match is complete - assert_eq!(Chess::player_elo(alice), 2029); - assert_eq!(Chess::player_elo(bob), 2371); - }); -} - -#[test] -fn check_elo_player_aborts() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - - let alice = account("Alice", 0, 0); - let bob = account("Bob", 0, 1); - - let bet_asset_id = AssetId::get(); - let bet_amount = AssetMinBalance::get() * 5; - - assert_ok!(Chess::create_match( - RuntimeOrigin::signed(bob), - alice, - MatchStyle::Bullet, - bet_asset_id, - bet_amount - )); - - let match_id = Chess::chess_match_id_from_nonce(0).unwrap(); - - assert_ok!(Chess::join_match(RuntimeOrigin::signed(alice), match_id)); - - // check elo before the match finishes - assert_eq!(Chess::player_elo(alice), 2000); - assert_eq!(Chess::player_elo(bob), 2400); - - assert_ok!(Chess::force_board_state( - match_id, - "8/8/8/8/8/5K2/Q7/7k w - - 1 68".into() - )); - // this move forces draws - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(bob), - match_id, - "a2f2".into() - )); - assert_eq!(Chess::chess_matches(match_id), None); - - // check the elo after the match is complete - assert_eq!(Chess::player_elo(alice), 2013); - assert_eq!(Chess::player_elo(bob), 2387); - }); -} - -const BOARD_STATE: &str = "Q7/5Q2/8/8/3k4/6P1/6BP/7K b - - 0 67"; - -#[test] -fn force_board_state_works() { - new_test_ext().execute_with(|| { - let alice = account("Alice", 0, 0); - let bob = account("Bob", 0, 1); - - let bet_asset_id = AssetId::get(); - let bet_amount = AssetMinBalance::get() * 5; // assuming T::IncentiveShare is 10% - - assert_ok!(Chess::create_match( - RuntimeOrigin::signed(alice), - bob, - MatchStyle::Bullet, - bet_asset_id, - bet_amount - )); - - let match_id = Chess::chess_match_id_from_nonce(0).unwrap(); - - assert_ok!(Chess::join_match(RuntimeOrigin::signed(bob), match_id)); - - assert_ok!(Chess::force_board_state(match_id, BOARD_STATE.into())); - - let chess_match = Chess::chess_matches(match_id).unwrap(); - assert_eq!(chess_match.board, BOARD_STATE.as_bytes()); - }); -} - -#[test] -fn claim_victory_works() { - new_test_ext().execute_with(|| { - let alice = account("Alice", 0, 0); - let bob = account("Bob", 0, 1); - - let bet_asset_id = AssetId::get(); - let bet_amount = AssetMinBalance::get() * 5; // assuming T::IncentiveShare is 10% - - let initial_balance_a = Assets::balance(bet_asset_id, alice); - let initial_balance_b = Assets::balance(bet_asset_id, bob); - - assert_ok!(Chess::create_match( - RuntimeOrigin::signed(alice), - bob, - MatchStyle::Bullet, - bet_asset_id, - bet_amount - )); - - let match_id = Chess::chess_match_id_from_nonce(0).unwrap(); - - assert_ok!(Chess::join_match(RuntimeOrigin::signed(bob), match_id)); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "e2e4".into() - )); - - // advance the block number to the point where bob's time-to-move is expired - System::set_block_number( - System::block_number() + ::BulletPeriod::get() + 1, - ); - - // alice claims victory - assert_ok!(Chess::clear_abandoned_match( - RuntimeOrigin::signed(alice), - match_id - )); - - System::assert_has_event( - Event::MatchWon { - 0: match_id, - 1: alice, - 2: "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1".into(), - } - .into(), - ); - - assert_eq!(Chess::chess_matches(match_id), None); - assert_eq!(Chess::chess_match_id_from_nonce(0), None); - - let final_balance_a = Assets::balance(bet_asset_id, alice); - let final_balance_b = Assets::balance(bet_asset_id, bob); - assert_eq!(final_balance_a, initial_balance_a + bet_amount); - assert_eq!(final_balance_b, initial_balance_b - bet_amount); - }); -} - -#[test] -fn janitor_incentive_works() { - new_test_ext().execute_with(|| { - let alice = account("Alice", 0, 0); - let bob = account("Bob", 0, 1); - let charlie = account("Charlie", 0, 2); - - let bet_asset_id = AssetId::get(); - let bet_amount = AssetMinBalance::get() * 5; // assuming T::IncentiveShare is 10% - - let initial_balance_a = Assets::balance(bet_asset_id, alice); - let initial_balance_b = Assets::balance(bet_asset_id, bob); - let initial_balance_c = Assets::balance(bet_asset_id, charlie); - - assert_ok!(Chess::create_match( - RuntimeOrigin::signed(alice), - bob, - MatchStyle::Bullet, - bet_asset_id, - bet_amount - )); - - let match_id = Chess::chess_match_id_from_nonce(0).unwrap(); - - assert_ok!(Chess::join_match(RuntimeOrigin::signed(bob), match_id)); - assert_ok!(Chess::make_move( - RuntimeOrigin::signed(alice), - match_id, - "e2e4".into() - )); - - let chess_match = Chess::chess_matches(match_id).unwrap(); - let (janitor_incentive, actual_prize) = chess_match.janitor_incentive(); - - // advance the block number to the point where bob's time-to-move is expired - // and alice's time to claim victory is also expired - System::set_block_number( - System::block_number() + ::BulletPeriod::get() * 10 + 1, - ); - - // charlie cleans abandoned match - assert_ok!(Chess::clear_abandoned_match( - RuntimeOrigin::signed(charlie), - match_id - )); - - System::assert_has_event( - Event::MatchWon { - 0: match_id, - 1: alice, - 2: "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1".into(), - } - .into(), - ); - - assert_eq!(Chess::chess_matches(match_id), None); - assert_eq!(Chess::chess_match_id_from_nonce(0), None); - - let final_balance_a = Assets::balance(bet_asset_id, alice); - let final_balance_b = Assets::balance(bet_asset_id, bob); - let final_balance_c = Assets::balance(bet_asset_id, charlie); - assert_eq!( - final_balance_a, - initial_balance_a - bet_amount + actual_prize - ); - assert_eq!(final_balance_b, initial_balance_b - bet_amount); - assert_eq!(final_balance_c, initial_balance_c + janitor_incentive); - }); -} +// #[test] +// fn check_elo_stronger_wins() { +// new_test_ext().execute_with(|| { +// System::set_block_number(1); + +// let alice = account("Alice", 0, 0); +// let bob = account("Bob", 0, 1); + +// let bet_asset_id = AssetId::get(); +// let bet_amount = AssetMinBalance::get() * 5; + +// assert_ok!(Chess::create_match( +// RuntimeOrigin::signed(alice), +// bob, +// MatchStyle::Bullet, +// bet_asset_id, +// bet_amount +// )); + +// let match_id = Chess::chess_match_id_from_nonce(0).unwrap(); + +// assert_ok!(Chess::join_match(RuntimeOrigin::signed(bob), match_id)); + +// // check elo before the match finishes +// assert_eq!(Chess::player_elo(alice), 2000); +// assert_eq!(Chess::player_elo(bob), 2400); + +// assert_ok!(Chess::make_move( +// RuntimeOrigin::signed(alice), +// match_id, +// "f2f3".into() +// )); +// assert_ok!(Chess::make_move( +// RuntimeOrigin::signed(bob), +// match_id, +// "e7e5".into() +// )); +// assert_ok!(Chess::make_move( +// RuntimeOrigin::signed(alice), +// match_id, +// "g2g4".into() +// )); +// assert_ok!(Chess::make_move( +// RuntimeOrigin::signed(bob), +// match_id, +// "d8h4".into() +// )); + +// assert_eq!(Chess::chess_matches(match_id), None); + +// // check the elo after match is complete +// assert_eq!(Chess::player_elo(alice), 1997); +// assert_eq!(Chess::player_elo(bob), 2403); +// }); +// } + +// #[test] +// fn check_elo_stronger_looses() { +// new_test_ext().execute_with(|| { +// System::set_block_number(1); + +// let alice = account("Alice", 0, 0); +// let bob = account("Bob", 0, 1); + +// let bet_asset_id = AssetId::get(); +// let bet_amount = AssetMinBalance::get() * 5; + +// assert_ok!(Chess::create_match( +// RuntimeOrigin::signed(bob), +// alice, +// MatchStyle::Bullet, +// bet_asset_id, +// bet_amount +// )); + +// let match_id = Chess::chess_match_id_from_nonce(0).unwrap(); + +// assert_ok!(Chess::join_match(RuntimeOrigin::signed(alice), match_id)); + +// // check elo before the match finishes +// assert_eq!(Chess::player_elo(alice), 2000); +// assert_eq!(Chess::player_elo(bob), 2400); + +// assert_ok!(Chess::make_move( +// RuntimeOrigin::signed(bob), +// match_id, +// "f2f3".into() +// )); +// assert_ok!(Chess::make_move( +// RuntimeOrigin::signed(alice), +// match_id, +// "e7e5".into() +// )); +// assert_ok!(Chess::make_move( +// RuntimeOrigin::signed(bob), +// match_id, +// "g2g4".into() +// )); +// assert_ok!(Chess::make_move( +// RuntimeOrigin::signed(alice), +// match_id, +// "d8h4".into() +// )); + +// assert_eq!(Chess::chess_matches(match_id), None); + +// // check the elo after match is complete +// assert_eq!(Chess::player_elo(alice), 2029); +// assert_eq!(Chess::player_elo(bob), 2371); +// }); +// } + +// #[test] +// fn check_elo_player_aborts() { +// new_test_ext().execute_with(|| { +// System::set_block_number(1); + +// let alice = account("Alice", 0, 0); +// let bob = account("Bob", 0, 1); + +// let bet_asset_id = AssetId::get(); +// let bet_amount = AssetMinBalance::get() * 5; + +// assert_ok!(Chess::create_match( +// RuntimeOrigin::signed(bob), +// alice, +// MatchStyle::Bullet, +// bet_asset_id, +// bet_amount +// )); + +// let match_id = Chess::chess_match_id_from_nonce(0).unwrap(); + +// assert_ok!(Chess::join_match(RuntimeOrigin::signed(alice), match_id)); + +// // check elo before the match finishes +// assert_eq!(Chess::player_elo(alice), 2000); +// assert_eq!(Chess::player_elo(bob), 2400); + +// assert_ok!(Chess::force_board_state( +// match_id, +// "8/8/8/8/8/5K2/Q7/7k w - - 1 68".into() +// )); +// // this move forces draws +// assert_ok!(Chess::make_move( +// RuntimeOrigin::signed(bob), +// match_id, +// "a2f2".into() +// )); +// assert_eq!(Chess::chess_matches(match_id), None); + +// // check the elo after the match is complete +// assert_eq!(Chess::player_elo(alice), 2013); +// assert_eq!(Chess::player_elo(bob), 2387); +// }); +// } + +// #[test] +// fn claim_victory_works() { +// new_test_ext().execute_with(|| { +// let alice = account("Alice", 0, 0); +// let bob = account("Bob", 0, 1); + +// let bet_asset_id = AssetId::get(); +// let bet_amount = AssetMinBalance::get() * 5; // assuming T::IncentiveShare is 10% + +// let initial_balance_a = Assets::balance(bet_asset_id, alice); +// let initial_balance_b = Assets::balance(bet_asset_id, bob); + +// assert_ok!(Chess::create_match( +// RuntimeOrigin::signed(alice), +// bob, +// MatchStyle::Bullet, +// bet_asset_id, +// bet_amount +// )); + +// let match_id = Chess::chess_match_id_from_nonce(0).unwrap(); + +// assert_ok!(Chess::join_match(RuntimeOrigin::signed(bob), match_id)); +// assert_ok!(Chess::make_move( +// RuntimeOrigin::signed(alice), +// match_id, +// "e2e4".into() +// )); + +// // advance the block number to the point where bob's time-to-move is expired +// System::set_block_number( +// System::block_number() + ::BulletPeriod::get() + 1, +// ); + +// // alice claims victory +// assert_ok!(Chess::clear_abandoned_match( +// RuntimeOrigin::signed(alice), +// match_id +// )); + +// System::assert_has_event( +// Event::MatchWon { +// 0: match_id, +// 1: alice, +// 2: "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1".into(), +// } +// .into(), +// ); + +// assert_eq!(Chess::chess_matches(match_id), None); +// assert_eq!(Chess::chess_match_id_from_nonce(0), None); + +// let final_balance_a = Assets::balance(bet_asset_id, alice); +// let final_balance_b = Assets::balance(bet_asset_id, bob); +// assert_eq!(final_balance_a, initial_balance_a + bet_amount); +// assert_eq!(final_balance_b, initial_balance_b - bet_amount); +// }); +// } + +// #[test] +// fn janitor_incentive_works() { +// new_test_ext().execute_with(|| { +// let alice = account("Alice", 0, 0); +// let bob = account("Bob", 0, 1); +// let charlie = account("Charlie", 0, 2); + +// let bet_asset_id = AssetId::get(); +// let bet_amount = AssetMinBalance::get() * 5; // assuming T::IncentiveShare is 10% + +// let initial_balance_a = Assets::balance(bet_asset_id, alice); +// let initial_balance_b = Assets::balance(bet_asset_id, bob); +// let initial_balance_c = Assets::balance(bet_asset_id, charlie); + +// assert_ok!(Chess::create_match( +// RuntimeOrigin::signed(alice), +// bob, +// MatchStyle::Bullet, +// bet_asset_id, +// bet_amount +// )); + +// let match_id = Chess::chess_match_id_from_nonce(0).unwrap(); + +// assert_ok!(Chess::join_match(RuntimeOrigin::signed(bob), match_id)); +// assert_ok!(Chess::make_move( +// RuntimeOrigin::signed(alice), +// match_id, +// "e2e4".into() +// )); + +// let chess_match = Chess::chess_matches(match_id).unwrap(); +// let (janitor_incentive, actual_prize) = chess_match.janitor_incentive(); + +// // advance the block number to the point where bob's time-to-move is expired +// // and alice's time to claim victory is also expired +// System::set_block_number( +// System::block_number() + ::BulletPeriod::get() * 10 + 1, +// ); + +// // charlie cleans abandoned match +// assert_ok!(Chess::clear_abandoned_match( +// RuntimeOrigin::signed(charlie), +// match_id +// )); + +// System::assert_has_event( +// Event::MatchWon { +// 0: match_id, +// 1: alice, +// 2: "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1".into(), +// } +// .into(), +// ); + +// assert_eq!(Chess::chess_matches(match_id), None); +// assert_eq!(Chess::chess_match_id_from_nonce(0), None); + +// let final_balance_a = Assets::balance(bet_asset_id, alice); +// let final_balance_b = Assets::balance(bet_asset_id, bob); +// let final_balance_c = Assets::balance(bet_asset_id, charlie); +// assert_eq!( +// final_balance_a, +// initial_balance_a - bet_amount + actual_prize +// ); +// assert_eq!(final_balance_b, initial_balance_b - bet_amount); +// assert_eq!(final_balance_c, initial_balance_c + janitor_incentive); +// }); +// } #[test] fn get_player_matches_works() { @@ -784,10 +482,10 @@ fn get_player_matches_works() { System::block_number() + ::BulletPeriod::get() + 1, ); - assert_ok!(Chess::clear_abandoned_match( - RuntimeOrigin::signed(alice), - match_id - )); + // assert_ok!(Chess::clear_abandoned_match( + // RuntimeOrigin::signed(alice), + // match_id + // )); alice_matches = PlayerMatches::::iter_key_prefix(alice).collect::>(); bob_matches = PlayerMatches::::iter_key_prefix(bob).collect::>();