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
- }
-}