From 2513fa2e17d2a2d4f699b0be705eb9dbe468901a Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Wed, 9 Apr 2025 22:05:57 +0200 Subject: [PATCH] Remove unused --- rust/mia-game/Cargo.toml | 31 -- rust/mia-game/README.md | 43 --- rust/mia-game/src/lib.rs | 572 -------------------------------- rust/mia-game/src/zk_compute.rs | 44 --- 4 files changed, 690 deletions(-) delete mode 100644 rust/mia-game/Cargo.toml delete mode 100644 rust/mia-game/README.md delete mode 100644 rust/mia-game/src/lib.rs delete mode 100644 rust/mia-game/src/zk_compute.rs diff --git a/rust/mia-game/Cargo.toml b/rust/mia-game/Cargo.toml deleted file mode 100644 index 0916d8f..0000000 --- a/rust/mia-game/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "mia-game" -readme = "README.md" -version.workspace = true -description.workspace = true -homepage.workspace = true -repository.workspace = true -documentation.workspace = true -edition.workspace = true -license.workspace = true - -[features] -abi = ["pbc_contract_common/abi", "pbc_contract_codegen/abi", "pbc_traits/abi", "create_type_spec_derive/abi", "pbc_lib/abi", "pbc_zk/abi"] -plus_metadata = [] - -[lib] -path = "src/lib.rs" -crate-type = ['rlib', 'cdylib'] - -[package.metadata.zk] -zk-compute-path = "src/zk_compute.rs" - -[dependencies] -pbc_contract_common.workspace = true -pbc_traits.workspace = true -pbc_lib.workspace = true -read_write_rpc_derive.workspace = true -read_write_state_derive.workspace = true -create_type_spec_derive.workspace = true -pbc_contract_codegen.workspace = true -pbc_zk.workspace = true diff --git a/rust/mia-game/README.md b/rust/mia-game/README.md deleted file mode 100644 index c5c6db6..0000000 --- a/rust/mia-game/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Mia Gaming Contract - -## Rules - -3 or more players, all start with 6 lives. - -The complete order of rolls (from highest to lowest): - -21 (Mia), 31 (Little Mia), 66, 55, 44, 33, 22, 11, 65, 64, 63, 62, 61, 54, 53, 52, 51, 43, 42, 41, 32 - -### Order of actions - -The first player rolls the dice and keeps their value concealed from the other players. - -The player then has three choices: - -- Tell the truth and announce what has been rolled. -- Lie and announce a greater value than that rolled. -- Lie and announce a lesser value. - -The concealed dice are then passed to the next player in a clockwise fashion. - -The receiving player now has two options: - -- Believe the passer, roll the dice and pass it on, announcing a higher value—with or without looking at them. -- Call the passer a liar and look at the dice. - If the dice show a lesser value than that announced, the passer loses a life and the receiving player starts a new round. - However, if the dice show a greater or equal value, the current player loses a life and the next player starts a new round. - -**Note:** -Each player must always announce a value greater than or equal to the previous value announced. - -##### Mia announced - -If Mia is announced, the next player has two choices: - -- They may give up without looking at the dice and lose one life. -- They may look at the dice. If it was a Mia, they lose two lives. - If it wasn't, the previous player loses two lives. - -### Winning the game - -Last remaining player is the winner. diff --git a/rust/mia-game/src/lib.rs b/rust/mia-game/src/lib.rs deleted file mode 100644 index 4783d7c..0000000 --- a/rust/mia-game/src/lib.rs +++ /dev/null @@ -1,572 +0,0 @@ -#![doc = include_str!("../README.md")] -#![allow(unused_variables)] - -#[macro_use] -extern crate pbc_contract_codegen; -extern crate pbc_contract_common; -extern crate pbc_lib; - -mod zk_compute; - -use create_type_spec_derive::CreateTypeSpec; -use pbc_contract_common::address::Address; -use pbc_contract_common::context::ContractContext; -use pbc_contract_common::events::EventGroup; -use pbc_contract_common::sorted_vec_map::{SortedVecMap, SortedVecSet}; -use pbc_contract_common::zk::{SecretVarId, ZkInputDef, ZkState, ZkStateChange}; -use pbc_traits::ReadWriteState; -use pbc_zk::{Sbi8, SecretBinary}; -use read_write_rpc_derive::ReadWriteRPC; -use read_write_state_derive::ReadWriteState; - -/** - * Metadata information associated with each individual variable. - */ -#[derive(ReadWriteState, ReadWriteRPC, Debug)] -#[repr(u8)] -pub enum SecretVarType { - #[discriminant(0)] - /// Randomness used for dice throws. - Randomness {}, - #[discriminant(1)] - /// Result of dice throws. - ThrowResult {}, -} - -/// The state of the Mia game, which is persisted on-chain. -#[state] -pub struct MiaState { - // The current amount of randomness contributions received for a dice throw. - nr_of_randomness_contributions: u32, - // The number of players at the start of the game. - nr_of_players_at_the_start: u32, - // The player currently throwing the dice and declaring a value. - player_throwing: u32, - // The current phase the game is in, to determine allowed actions. - game_phase: GamePhase, - // The players at the start of the game. - starting_players: Vec
, - // The current players active in the game. - players: Vec
, - // The remaining lives of the players in the game. - player_lives: SortedVecMap, - // The last throw's secret variable id. - throw_result_id: Option, - // The stated value of the current throw. - stated_throw: Option, - // The revealed value of a throw. - throw_result: Option, - // The announced throw value, where the next announced throw must be higher than, to be eligible. - throw_to_beat: DiceThrow, - // The winner of the game. - winner: Option
, -} - -impl MiaState { - /// Get the current player in turn. - fn current_player(&self) -> &Address { - &self.players[self.player_throwing as usize] - } - /// Get the next player in turn. - fn next_player(&self) -> &Address { - &self.players[(self.player_throwing + 1) as usize % self.players.len()] - } - - /// Replace the current player in turn with the next player. - fn go_to_next_player(&mut self) { - self.player_throwing = (self.player_throwing + 1) % self.players.len() as u32; - } - - /// Check whether a player is dead i.e. have no lives left. - fn is_player_dead(&self, player: Address) -> bool { - self.player_lives[&player] == 0 - } - - /// Remove a dead player from the list of players. - fn remove_dead_player(&mut self, player: Address) { - self.players.retain(|p| player != *p); - } - - /// Reduce a players lives by a given integer. - fn reduce_players_life_by(&mut self, player: Address, lives_lost: u8) { - if self.player_lives[&player] >= lives_lost { - self.player_lives - .insert(player, self.player_lives[&player] - lives_lost); - } else { - self.player_lives.insert(player, 0); - } - } - - /// Check whether the game if finished, i.e. whether only one player remains. - fn is_the_game_finished(&self) -> bool { - self.players.len() == 1 - } - - /// Get the last remaining player, the winner. - fn get_winner(&self) -> Address { - *self.players.first().unwrap() - } -} - -/// A throw of two dice. -#[derive(ReadWriteState, ReadWriteRPC, CreateTypeSpec, Debug, Copy, Clone)] -pub struct DiceThrow { - d1: u8, - d2: u8, -} - -impl DiceThrow { - /// The value of each die is reduced to be between 0 and 5. - fn reduce(&self) -> DiceThrow { - DiceThrow { - d1: self.d1 % 6, - d2: self.d2 % 6, - } - } - - /// Checks whether a throw is better than the current dice throw to beat. - /// The dice throws are compared based on their associated values. - fn better_than_or_equal(self, actual: DiceThrow) -> bool { - self.get_throw_score() >= actual.get_throw_score() - } - - /// Checks whether a dice throw is Mia, i.e. is (0,1) or (1,0). - fn is_mia(self) -> bool { - (self.d1 == 0 && self.d2 == 1) || (self.d2 == 0 && self.d1 == 1) - } - - /// Checks whether a dice throw is Little Mia, i.e. is (0,2) or (2,0). - fn is_little_mia(self) -> bool { - (self.d1 == 0 && self.d2 == 2) || (self.d2 == 0 && self.d1 == 2) - } - - /// Checks whether both dices in the dice throw have the same value. - fn is_pair(self) -> bool { - self.d1 == self.d2 - } - - /// Get the score of a dice throw. - /// The throw values are determined such that the highest roll is Mia, then Little Mia, - /// followed by the doubles from (5,5) to (0,0), and then all other rolls from (5,4) - /// down to (2,1). - fn get_throw_score(self) -> u8 { - let mut value = 0; - if self.is_mia() { - value += 128; - }; - if self.is_little_mia() { - value += 64; - }; - if self.is_pair() { - value += 32; - }; - if (self.d1 == 5) || (self.d2 == 5) { - value += 16; - } - if (self.d1 == 4) || (self.d2 == 4) { - value += 8; - } - if (self.d1 == 3) || (self.d2 == 3) { - value += 4; - } - if (self.d1 == 2) || (self.d2 == 2) { - value += 2; - } - if (self.d1 == 1) || (self.d2 == 1) { - value += 1; - } - value - } -} - -/// The contribution each player must send to make a dice throw. The contributions should be in the -/// interval \[ 0, 5 \] inclusive. If the contributions are outside this interval, -/// they are normalized to the interval. -#[derive(CreateTypeSpec, SecretBinary)] -pub struct RandomContribution { - d1: Sbi8, - d2: Sbi8, -} - -/// The different phases the contract can be in before, during and after a game of Mia. -#[derive(ReadWriteRPC, ReadWriteState, CreateTypeSpec, Debug, PartialEq, Copy, Clone)] -pub enum GamePhase { - #[discriminant(0)] - /// The game has been initialized. - Start {}, - #[discriminant(1)] - /// Players can add randomness as secret inputs. - AddRandomness {}, - #[discriminant(2)] - /// The player in turn can throw the dice. - Throw {}, - #[discriminant(3)] - /// The player in turn can announce their throw. - Announce {}, - #[discriminant(4)] - /// The next player in turn can believe or call out the player's announced throw. - Decide {}, - #[discriminant(5)] - /// If a player was called out, they can reveal their actual throw. - Reveal {}, - #[discriminant(6)] - /// The game is finished. - Done {}, -} - -/// Initialize a new mia game. -/// -/// # Arguments -/// -/// * `_ctx` - the contract context containing information about the sender and the blockchain. -/// * -/// -/// # Returns -/// -/// The initial state of the petition, with no signers. -/// -#[init(zk = true)] -pub fn initialize( - context: ContractContext, - zk_state: ZkState, - addresses_to_play: Vec
, -) -> (MiaState, Vec) { - assert!( - addresses_to_play.len() >= 3, - "There must be at least 3 players to play Mia." - ); - assert_eq!( - SortedVecSet::from(addresses_to_play.clone()).len(), - addresses_to_play.len(), - "No duplicates in players." - ); - - let mut state = MiaState { - starting_players: addresses_to_play.clone(), - players: addresses_to_play.clone(), - nr_of_players_at_the_start: addresses_to_play.len() as u32, - player_lives: SortedVecMap::new(), - game_phase: GamePhase::Start {}, - player_throwing: 0, - nr_of_randomness_contributions: 0, - throw_result_id: None, - stated_throw: None, - throw_result: None, - winner: None, - throw_to_beat: DiceThrow { d1: 1, d2: 2 }, - }; - - for address in addresses_to_play { - state.player_lives.insert(address, 6); - } - - (state, vec![]) -} - -/// Start the game. -/// -/// # Arguments -/// -/// * `ctx` - the contract context containing information about the sender and the blockchain. -/// * `state` - the current state of the game. -/// -/// # Returns -/// -/// The updated vote state reflecting the new signing. -/// -#[action(shortname = 0x01, zk = true)] -pub fn start_round( - context: ContractContext, - mut state: MiaState, - zk_state: ZkState, -) -> (MiaState, Vec, Vec) { - assert_eq!( - state.players[state.player_throwing as usize], context.sender, - "Only the player whose turn it is can start the round." - ); - state.game_phase = GamePhase::AddRandomness {}; - - (state, vec![], vec![]) -} - -/// Add randomness for the next dice throw. -/// The sender must be a player in the game to add randomness. -#[zk_on_secret_input(shortname = 0x40, secret_type = "RandomContribution")] -pub fn add_randomness_to_throw( - context: ContractContext, - state: MiaState, - zk_state: ZkState, -) -> ( - MiaState, - Vec, - ZkInputDef, -) { - assert_eq!( - state.game_phase, - GamePhase::AddRandomness {}, - "Must be in the AddRandomness phase to input secret randomness." - ); - assert!(state.starting_players.contains(&context.sender)); - assert!( - zk_state - .secret_variables - .iter() - .chain(zk_state.pending_inputs.iter()) - .all(|(_, secret_variable)| secret_variable.owner != context.sender), - "Each Player is only allowed to send one contribution to the randomness of the dice throw. Sender: {:?}", - context.sender - ); - - let input_def = ZkInputDef::with_metadata( - Some(SHORTNAME_INPUTTED_VARIABLE), - SecretVarType::Randomness {}, - ); - - (state, vec![], input_def) -} - -/// Automatically called when a variable is confirmed on chain. -/// -/// Initializes opening. -#[zk_on_variable_inputted(shortname = 0x01)] -fn inputted_variable( - context: ContractContext, - mut state: MiaState, - zk_state: ZkState, - variable_id: SecretVarId, -) -> MiaState { - if state.nr_of_randomness_contributions == state.nr_of_players_at_the_start - 1 { - state.nr_of_randomness_contributions = 0; - state.game_phase = GamePhase::Throw {}; - } else { - state.nr_of_randomness_contributions += 1; - } - state -} - -/// Start the computation to compute the dice throw. -#[action(shortname = 0x02, zk = true)] -pub fn throw_dice( - context: ContractContext, - state: MiaState, - zk_state: ZkState, -) -> (MiaState, Vec, Vec) { - assert_eq!( - state.game_phase, - GamePhase::Throw {}, - "The dice can only be thrown in the Throw phase" - ); - assert_eq!( - *state.current_player(), - context.sender, - "Only the player in turn can throw the dice. It is currently {:?}s turn", - state.current_player() - ); - - ( - state, - vec![], - vec![zk_compute::compute_dice_throw_start( - Some(SHORTNAME_SUM_COMPUTE_COMPLETE), - &SecretVarType::ThrowResult {}, - )], - ) -} - -/// Automatically called when the sum of the random contributions are done. -/// Transfers the resulting throw to the player throwing the dice. -#[zk_on_compute_complete(shortname = 0x01)] -fn sum_compute_complete( - _context: ContractContext, - mut state: MiaState, - zk_state: ZkState, - output_variables: Vec, -) -> (MiaState, Vec, Vec) { - let Some(result_id) = output_variables.first() else { - panic!("No result") - }; - - state.throw_result_id = Some(*result_id); - state.game_phase = GamePhase::Announce {}; - let player_to_transfer_to = *state.current_player(); - - ( - state, - vec![], - vec![ - ZkStateChange::TransferVariable { - variable: *result_id, - new_owner: player_to_transfer_to, - }, - ZkStateChange::DeleteVariables { - variables_to_delete: zk_state - .secret_variables - .iter() - .map(|(variable_id, _)| variable_id) - .filter(|id| id != result_id) - .collect(), - }, - ], - ) -} - -/// Announce a value such that the next player can decide if they believe it or not. -/// The value must be higher than or equal to the throw to beat. -#[action(shortname = 0x03, zk = true)] -fn announce_throw( - context: ContractContext, - mut state: MiaState, - zk_state: ZkState, - dice_value: DiceThrow, -) -> (MiaState, Vec, Vec) { - assert_eq!( - *state.current_player(), - context.sender, - "Only the current player can state the value of the dice throw." - ); - - let reduced_dice_value = dice_value.reduce(); - - if !reduced_dice_value.better_than_or_equal(state.throw_to_beat) { - panic!("Stated throw must be better than the last stated throw.") - } - - state.stated_throw = Some(dice_value); - state.game_phase = GamePhase::Decide {}; - - (state, vec![], vec![]) -} - -/// The next player believes the stated throw, and continues the round, where the throw to beat is -/// the stated throw. -#[action(shortname = 0x04, zk = true)] -fn believe( - context: ContractContext, - mut state: MiaState, - zk_state: ZkState, -) -> (MiaState, Vec, Vec) { - assert_eq!( - context.sender, - *state.next_player(), - "Only the next player can say if they believe the stated throw." - ); - assert_eq!( - state.game_phase, - GamePhase::Decide {}, - "Must be in the deciding phase to say believe." - ); - - state.game_phase = GamePhase::AddRandomness {}; - state.throw_to_beat = state.stated_throw.unwrap(); - state.stated_throw = None; - state.go_to_next_player(); - - ( - state, - vec![], - vec![ZkStateChange::DeleteVariables { - variables_to_delete: zk_state - .secret_variables - .iter() - .map(|(variable_id, _)| variable_id) - .collect(), - }], - ) -} - -/// The next player does not believe the stated throw, and starts a reveal of the dice. -#[action(shortname = 0x05, zk = true)] -fn call_out( - context: ContractContext, - mut state: MiaState, - zk_state: ZkState, -) -> (MiaState, Vec, Vec) { - assert_eq!( - context.sender, - *state.next_player(), - "Only the next player can say if the throwing player is lying." - ); - assert_eq!( - state.game_phase, - GamePhase::Decide {}, - "Must be in the deciding phase say if the throwing player is lying." - ); - let variable_to_open = state.throw_result_id.unwrap(); - state.game_phase = GamePhase::Reveal {}; - ( - state, - vec![], - vec![ZkStateChange::OpenVariables { - variables: vec![variable_to_open], - }], - ) -} - -/// Saves the opened variable in state and readies another computation. -#[zk_on_variables_opened] -fn save_opened_variable( - context: ContractContext, - mut state: MiaState, - zk_state: ZkState, - opened_variables: Vec, -) -> (MiaState, Vec, Vec) { - assert_eq!( - opened_variables.len(), - 1, - "Can only show one set of dice at a time." - ); - - let variable_id = opened_variables.first().unwrap(); - let result: DiceThrow = read_opened_variable_data(&zk_state, variable_id).unwrap(); - - let result_reduced = result.reduce(); - - let Some(stated_throw) = state.stated_throw else { - panic!("Could not find a stated throw in state.") - }; - - let stated_throw_reduced = stated_throw.reduce(); - - let loser_of_round = if result.better_than_or_equal(stated_throw_reduced) { - *state.next_player() - } else { - *state.current_player() - }; - - if stated_throw.is_mia() { - state.reduce_players_life_by(loser_of_round, 2); - } else { - state.reduce_players_life_by(loser_of_round, 1); - } - - if state.is_player_dead(loser_of_round) { - state.remove_dead_player(loser_of_round); - } - - state.throw_result = Some(result_reduced); - - if state.is_the_game_finished() { - state.game_phase = GamePhase::Done {}; - state.winner = Some(state.get_winner()); - } else { - state.go_to_next_player(); - state.game_phase = GamePhase::AddRandomness {}; - } - - ( - state, - vec![], - vec![ZkStateChange::DeleteVariables { - variables_to_delete: opened_variables, - }], - ) -} - -/// Reads the data from a revealed secret variable -fn read_opened_variable_data( - zk_state: &ZkState, - variable_id: &SecretVarId, -) -> Option { - let variable = zk_state.get_variable(*variable_id)?; - variable.open_value() -} diff --git a/rust/mia-game/src/zk_compute.rs b/rust/mia-game/src/zk_compute.rs deleted file mode 100644 index 3ed9087..0000000 --- a/rust/mia-game/src/zk_compute.rs +++ /dev/null @@ -1,44 +0,0 @@ -use pbc_zk::*; - -/// Output variable type -#[derive(pbc_zk::SecretBinary, Clone)] -struct RandomnessInput { - /// Token amount. - d1: Sbi8, - d2: Sbi8, -} - -/// Perform a zk computation on secret-shared randomness added to make a random dice throw. -/// -/// ### Returns: -/// -/// The sum of the randomness contributions variables. -#[zk_compute(shortname = 0x61)] -pub fn compute_dice_throw() -> RandomnessInput { - let mut throw = RandomnessInput { - d1: Sbi8::from(0), - d2: Sbi8::from(0), - }; - - for variable_id in secret_variable_ids() { - let mut raw_contribution: RandomnessInput = load_sbi::(variable_id); - - let d1_reduced = reduce_contribution(raw_contribution.d1); - let d2_reduced = reduce_contribution(raw_contribution.d2); - - throw.d1 = throw.d1 + d1_reduced; - throw.d2 = throw.d2 + d2_reduced; - } - - throw -} - -/// Reduce the contribution if it is not between 0 and 5. -fn reduce_contribution(value: Sbi8) -> Sbi8 { - let reduce = value & Sbi8::from(0b111); - if reduce >= Sbi8::from(6) { - reduce - Sbi8::from(6) - } else { - reduce - } -}