From 546e4c5696c5a8c40922bd759f8c99dd7425356c Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Mon, 29 Oct 2018 21:11:58 -0700 Subject: [PATCH] Remove bpf tictactoe --- build.rs | 2 - programs/bpf/c/src/tictactoe.c | 231 ------------------- programs/bpf/c/src/tictactoe.h | 36 --- programs/bpf/c/src/tictactoe_dashboard.c | 98 -------- tests/programs.rs | 275 ----------------------- 5 files changed, 642 deletions(-) delete mode 100644 programs/bpf/c/src/tictactoe.c delete mode 100644 programs/bpf/c/src/tictactoe.h delete mode 100644 programs/bpf/c/src/tictactoe_dashboard.c diff --git a/build.rs b/build.rs index 0f2f90ab3..885d1879a 100644 --- a/build.rs +++ b/build.rs @@ -29,8 +29,6 @@ fn main() { println!("cargo:rerun-if-changed=programs/bpf/c/makefile"); println!("cargo:rerun-if-changed=programs/bpf/c/src/move_funds.c"); println!("cargo:rerun-if-changed=programs/bpf/c/src/noop.c"); - println!("cargo:rerun-if-changed=programs/bpf/c/src/tictactoe.c"); - println!("cargo:rerun-if-changed=programs/bpf/c/src/tictactoe_dashboard.c"); println!("cargo:warning=(not a warning) Compiling C-based BPF programs"); let status = Command::new("make") .current_dir("programs/bpf/c") diff --git a/programs/bpf/c/src/tictactoe.c b/programs/bpf/c/src/tictactoe.c deleted file mode 100644 index 76beb863d..000000000 --- a/programs/bpf/c/src/tictactoe.c +++ /dev/null @@ -1,231 +0,0 @@ -/** - * @brief TicTacToe Dashboard C-based BPF program - */ - -#include -#include "tictactoe.h" - -typedef enum { - Result_Ok, - Result_Panic, - Result_GameInProgress, - Result_InvalidArguments, - Result_InvalidMove, - Result_InvalidUserdata, - Result_InvalidTimestamp, - Result_NoGame, - Result_NotYourTurn, - Result_PlayerNotFound, - Result_UserdataTooSmall, -} Result; - -typedef enum { - Command_Init = 0, - Command_Join, - Command_KeepAlive, - Command_Move, -} Command; - -SOL_FN_PREFIX void game_dump_board(Game *self) { - sol_print(0x9, 0x9, 0x9, 0x9, 0x9); - sol_print(0, 0, self->board[0], self->board[1], self->board[2]); - sol_print(0, 0, self->board[3], self->board[4], self->board[5]); - sol_print(0, 0, self->board[6], self->board[7], self->board[8]); - sol_print(0x9, 0x9, 0x9, 0x9, 0x9); -} - -SOL_FN_PREFIX void game_create(Game *self, SolPubkey *player_x) { - // account memory is zero-initialized - sol_memcpy(self->player_x.x, player_x, SIZE_PUBKEY); - self->state = State_Waiting; - for (int i = 0; i < 9; i++) { - self->board[i] = BoardItem_F; - } -} - -SOL_FN_PREFIX Result game_join(Game *self, SolPubkey *player_o, - int64_t timestamp) { - if (self->state == State_Waiting) { - sol_memcpy(self->player_o.x, player_o, SIZE_PUBKEY); - self->state = State_XMove; - - if (timestamp <= self->keep_alive[1]) { - return Result_InvalidTimestamp; - } else { - self->keep_alive[1] = timestamp; - return Result_Ok; - } - } - return Result_GameInProgress; -} - -SOL_FN_PREFIX bool game_same(BoardItem x_or_o, BoardItem one, BoardItem two, - BoardItem three) { - if (x_or_o == one && x_or_o == two && x_or_o == three) { - return true; - } - return false; -} - -SOL_FN_PREFIX bool game_same_player(SolPubkey *one, SolPubkey *two) { - for (int i = 0; i < SIZE_PUBKEY; i++) { - if (one->x[i] != two->x[i]) { - return false; - } - } - return true; -} - -SOL_FN_PREFIX Result game_next_move(Game *self, SolPubkey *player, int x, - int y) { - int board_index = y * 3 + x; - if (board_index >= 9 || self->board[board_index] != BoardItem_F) { - return Result_InvalidMove; - } - - BoardItem x_or_o; - State won_state; - - switch (self->state) { - case State_XMove: - if (!game_same_player(player, &self->player_x)) { - return Result_PlayerNotFound; - } - self->state = State_OMove; - x_or_o = BoardItem_X; - won_state = State_XWon; - break; - - case State_OMove: - if (!game_same_player(player, &self->player_o)) { - return Result_PlayerNotFound; - } - self->state = State_XMove; - x_or_o = BoardItem_O; - won_state = State_OWon; - break; - - default: - return Result_NotYourTurn; - } - - self->board[board_index] = x_or_o; - - // game_dump_board(self); - - bool winner = - // Check rows - game_same(x_or_o, self->board[0], self->board[1], self->board[2]) || - game_same(x_or_o, self->board[3], self->board[4], self->board[5]) || - game_same(x_or_o, self->board[6], self->board[7], self->board[8]) || - // Check columns - game_same(x_or_o, self->board[0], self->board[3], self->board[6]) || - game_same(x_or_o, self->board[1], self->board[4], self->board[7]) || - game_same(x_or_o, self->board[2], self->board[5], self->board[8]) || - // Check both diagonals - game_same(x_or_o, self->board[0], self->board[4], self->board[8]) || - game_same(x_or_o, self->board[2], self->board[4], self->board[6]); - - if (winner) { - self->state = won_state; - } - - { - int draw = true; - for (int i = 0; i < 9; i++) { - if (BoardItem_F == self->board[i]) { - draw = false; - break; - } - } - if (draw) { - self->state = State_Draw; - } - } - return Result_Ok; -} - -SOL_FN_PREFIX Result game_keep_alive(Game *self, SolPubkey *player, - int64_t timestamp) { - switch (self->state) { - case State_Waiting: - case State_XMove: - case State_OMove: - if (game_same_player(player, &self->player_x)) { - if (timestamp <= self->keep_alive[0]) { - return Result_InvalidTimestamp; - } - self->keep_alive[0] = timestamp; - } else if (game_same_player(player, &self->player_o)) { - if (timestamp <= self->keep_alive[1]) { - return Result_InvalidTimestamp; - } - self->keep_alive[1] = timestamp; - } else { - return Result_PlayerNotFound; - } - break; - - default: - break; - } - return Result_Ok; -} - -/** - * Number of SolKeyedAccounts expected. The program should bail if an - * unexpected number of accounts are passed to the program's entrypoint - * - * accounts[0] On Init must be player X, after that doesn't matter, - * anybody can cause a dashboard update - * accounts[1] must be a TicTacToe state account - * accounts[2] must be account of current player, only Pubkey is used - */ -#define NUM_KA 3 - -extern bool entrypoint(const uint8_t *input) { - SolKeyedAccounts ka[NUM_KA]; - uint8_t *data; - uint64_t data_len; - int err = 0; - - if (!sol_deserialize(input, NUM_KA, ka, &data, &data_len)) { - return false; - } - - if (sizeof(Game) > ka[1].userdata_len) { - sol_print(0, 0, 0xFF, sizeof(Game), ka[2].userdata_len); - return false; - } - Game game; - sol_memcpy(&game, ka[1].userdata, sizeof(game)); - - Command command = *data; - switch (command) { - case Command_Init: - game_create(&game, ka[2].key); - break; - - case Command_Join: - err = game_join(&game, ka[2].key, *((int64_t *)(data + 4))); - break; - - case Command_KeepAlive: - err = game_keep_alive(&game, ka[2].key, /*TODO*/ 0); - break; - - case Command_Move: - err = game_next_move(&game, ka[2].key, data[4], data[5]); - break; - - default: - return false; - } - - sol_memcpy(ka[1].userdata, &game, sizeof(game)); - sol_print(0, 0, 0, err, game.state); - if (Result_Ok != err) { - return false; - } - return true; -} diff --git a/programs/bpf/c/src/tictactoe.h b/programs/bpf/c/src/tictactoe.h deleted file mode 100644 index 6035bd60f..000000000 --- a/programs/bpf/c/src/tictactoe.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef TICTACTOE_H -#define TICTACTOE_H -/** - * @brief Definitions common to tictactoe and tictactoe_dashboard - */ - -typedef enum { - State_Waiting, - State_XMove, - State_OMove, - State_XWon, - State_OWon, - State_Draw, -} State; - -typedef enum { BoardItem_F, BoardItem_X, BoardItem_O } BoardItem; - -/** - * Game state - * - * This structure is stored in the owner's account userdata - * - * Board Coordinates - * | 0,0 | 1,0 | 2,0 | - * | 0,1 | 1,1 | 2,1 | - * | 0,2 | 1,2 | 2,2 | - */ -typedef struct { - SolPubkey player_x; /** Player who initialized the game */ - SolPubkey player_o; /** Player who joined the game */ - State state; /** Current state of the game */ - BoardItem board[9]; /** Tracks the player moves */ - int64_t keep_alive[2]; /** Keep Alive for each player */ -} Game; - -#endif // TICTACTOE_H diff --git a/programs/bpf/c/src/tictactoe_dashboard.c b/programs/bpf/c/src/tictactoe_dashboard.c deleted file mode 100644 index a8bd0fe97..000000000 --- a/programs/bpf/c/src/tictactoe_dashboard.c +++ /dev/null @@ -1,98 +0,0 @@ -/** - * @brief TicTacToe C-based BPF program - */ - -#include -#include "tictactoe.h" - -#define MAX_GAMES_TRACKED 5 - -/** - * Dashboard state - * - * This structure is stored in the owner's account userdata - */ -typedef struct { - SolPubkey pending; /** Latest pending game */ - SolPubkey completed[MAX_GAMES_TRACKED]; /** Last N completed games (0 is the - latest) */ - uint32_t latest_game; /** Index into completed pointing to latest game completed */ - uint32_t total; /** Total number of completed games */ -} Dashboard; - -SOL_FN_PREFIX bool update(Dashboard *self, Game *game, SolPubkey *game_pubkey) { - switch (game->state) { - case State_Waiting: - sol_memcpy(&self->pending, game_pubkey, SIZE_PUBKEY); - break; - case State_XMove: - case State_OMove: - // Nothing to do. In progress games are not managed by the dashboard - break; - case State_XWon: - case State_OWon: - case State_Draw: - for (int i = 0; i < MAX_GAMES_TRACKED; i++) { - if (SolPubkey_same(&self->completed[i], game_pubkey)) { - // TODO: Once the PoH height is exposed to programs, it could be used - // to ensure - // that old games are not being re-added and causing total to - // increment incorrectly. - return false; - } - } - self->total += 1; - self->latest_game = (self->latest_game + 1) % MAX_GAMES_TRACKED; - sol_memcpy(self->completed[self->latest_game].x, game_pubkey, - SIZE_PUBKEY); - break; - - default: - break; - } - return true; -} - -/** - * Number of SolKeyedAccounts expected. The program should bail if an - * unexpected number of accounts are passed to the program's entrypoint - * - * accounts[0] doesn't matter, anybody can cause a dashboard update - * accounts[1] must be a Dashboard account - * accounts[2] must be a Game account - */ -#define NUM_KA 3 - -extern bool entrypoint(const uint8_t *input) { - SolKeyedAccounts ka[NUM_KA]; - uint8_t *data; - uint64_t data_len; - int err = 0; - - if (!sol_deserialize(input, NUM_KA, ka, &data, &data_len)) { - return false; - } - - // TODO check dashboard and game program ids (how to check now that they are - // not known values) - // TODO check validity of dashboard and game structures contents - if (sizeof(Dashboard) > ka[1].userdata_len) { - sol_print(0, 0, 0xFF, sizeof(Dashboard), ka[2].userdata_len); - return false; - } - Dashboard dashboard; - sol_memcpy(&dashboard, ka[1].userdata, sizeof(dashboard)); - - if (sizeof(Game) > ka[2].userdata_len) { - sol_print(0, 0, 0xFF, sizeof(Game), ka[2].userdata_len); - return false; - } - Game game; - sol_memcpy(&game, ka[2].userdata, sizeof(game)); - if (true != update(&dashboard, &game, ka[2].key)) { - return false; - } - - sol_memcpy(ka[1].userdata, &dashboard, sizeof(dashboard)); - return true; -} diff --git a/tests/programs.rs b/tests/programs.rs index 88ece0efe..894bb3d6f 100644 --- a/tests/programs.rs +++ b/tests/programs.rs @@ -5,7 +5,6 @@ extern crate solana; extern crate solana_sdk; use bincode::serialize; -use serde_derive::Serialize; use solana::bank::Bank; #[cfg(feature = "bpf_c")] use solana::bpf_loader; @@ -330,277 +329,3 @@ fn test_program_bpf_noop_c() { loader.bank.process_transactions(&vec![tx.clone()]), ); } - -#[derive(Debug, Serialize)] -#[repr(C)] -pub enum Command { - Init, // player X initializes a new game - Join(i64), // player O wants to join (seconds since UNIX epoch) - KeepAlive(i64), // player X/O keep alive (seconds since UNIX epoch) - Move(u8, u8), // player X/O mark board position (x, y) -} - -#[cfg(feature = "bpf_c")] -struct TicTacToe { - game: Keypair, -} - -#[cfg(feature = "bpf_c")] -impl TicTacToe { - pub fn new(loader: &Loader, program: &Program) -> Self { - let game = Keypair::new(); - - // Create game account - let tx = Transaction::system_create( - &loader.mint.keypair(), - game.pubkey(), - loader.mint.last_id(), - 1, - 0x78, // corresponds to the C structure size - program.program.pubkey(), - 0, - ); - check_tx_results( - &loader.bank, - &tx, - loader.bank.process_transactions(&vec![tx.clone()]), - ); - - TicTacToe { game } - } - - pub fn id(&self) -> Pubkey { - self.game.pubkey().clone() - } - - pub fn init(&self, loader: &Loader, program: &Program, player: &Pubkey) { - let userdata = serialize(&Command::Init).unwrap(); - let tx = Transaction::new( - &self.game, - &[self.game.pubkey(), *player], - program.program.pubkey(), - userdata, - loader.mint.last_id(), - 0, - ); - check_tx_results( - &loader.bank, - &tx, - loader.bank.process_transactions(&vec![tx.clone()]), - ); - } - - pub fn command(&self, loader: &Loader, program: &Program, command: Command, player: &Pubkey) { - let userdata = serialize(&command).unwrap(); - let tx = Transaction::new( - &loader.mint.keypair(), - &[self.game.pubkey(), *player], - program.program.pubkey(), - userdata, - loader.mint.last_id(), - 0, - ); - check_tx_results( - &loader.bank, - &tx, - loader.bank.process_transactions(&vec![tx.clone()]), - ); - } - - pub fn get_player_x(&self, loader: &Loader) -> Vec { - loader - .bank - .get_account(&self.game.pubkey()) - .unwrap() - .userdata[0..32] - .to_vec() - } - - pub fn get_player_y(&self, loader: &Loader) -> Vec { - loader - .bank - .get_account(&self.game.pubkey()) - .unwrap() - .userdata[32..64] - .to_vec() - } - - pub fn game(&self, loader: &Loader) -> Vec { - loader - .bank - .get_account(&self.game.pubkey()) - .unwrap() - .userdata[64..68] - .to_vec() - } -} - -#[cfg(feature = "bpf_c")] -struct Dashboard { - dashboard: Keypair, -} - -#[cfg(feature = "bpf_c")] -impl Dashboard { - pub fn new(loader: &Loader, program: &Program) -> Self { - let dashboard = Keypair::new(); - - // Create game account - let tx = Transaction::system_create( - &loader.mint.keypair(), - dashboard.pubkey(), - loader.mint.last_id(), - 1, - 0xD0, // corresponds to the C structure size - program.program.pubkey(), - 0, - ); - check_tx_results( - &loader.bank, - &tx, - loader.bank.process_transactions(&vec![tx.clone()]), - ); - - Dashboard { dashboard } - } - - pub fn update(&self, loader: &Loader, program: &Program, game: &Pubkey) { - let tx = Transaction::new( - &self.dashboard, - &[self.dashboard.pubkey(), *game], - program.program.pubkey(), - vec![], - loader.mint.last_id(), - 0, - ); - check_tx_results( - &loader.bank, - &tx, - loader.bank.process_transactions(&vec![tx.clone()]), - ); - } - - pub fn get_game(&self, loader: &Loader, since_last: usize) -> Vec { - let userdata = loader - .bank - .get_account(&self.dashboard.pubkey()) - .unwrap() - .userdata; - - // TODO serialize - let last_game = userdata[192] as usize; - let this_game = (last_game + since_last * 4) % 5; - let start = 32 + this_game * 32; - let end = start + 32; - - loader - .bank - .get_account(&self.dashboard.pubkey()) - .unwrap() - .userdata[start..end] - .to_vec() - } - - pub fn get_pending(&self, loader: &Loader) -> Vec { - loader - .bank - .get_account(&self.dashboard.pubkey()) - .unwrap() - .userdata[0..32] - .to_vec() - } -} - -#[cfg(feature = "bpf_c")] -#[test] -fn test_program_bpf_tictactoe_c() { - logger::setup(); - - let loader = Loader::new_dynamic("bpf_loader"); - let program = Program::new( - &loader, - elf::File::open_path(&create_bpf_path("tictactoe")) - .unwrap() - .get_section(PLATFORM_SECTION_C) - .unwrap() - .data - .clone(), - ); - let player_x = Pubkey::new(&[0xA; 32]); - let player_y = Pubkey::new(&[0xB; 32]); - - let ttt = TicTacToe::new(&loader, &program); - ttt.init(&loader, &program, &player_x); - ttt.command(&loader, &program, Command::Join(0xAABBCCDD), &player_y); - ttt.command(&loader, &program, Command::Move(1, 1), &player_x); - ttt.command(&loader, &program, Command::Move(0, 0), &player_y); - ttt.command(&loader, &program, Command::Move(2, 0), &player_x); - ttt.command(&loader, &program, Command::Move(0, 2), &player_y); - ttt.command(&loader, &program, Command::Move(2, 2), &player_x); - ttt.command(&loader, &program, Command::Move(0, 1), &player_y); - - assert_eq!(player_x.as_ref(), &ttt.get_player_x(&loader)[..]); // validate x's key - assert_eq!(player_y.as_ref(), &ttt.get_player_y(&loader)[..]); // validate o's key - assert_eq!([4, 0, 0, 0], ttt.game(&loader)[..]); // validate that o won -} - -#[cfg(feature = "bpf_c")] -#[test] -fn test_program_bpf_tictactoe_dashboard_c() { - logger::setup(); - - let loader = Loader::new_dynamic("bpf_loader"); - let ttt_program = Program::new( - &loader, - elf::File::open_path(&create_bpf_path("tictactoe")) - .unwrap() - .get_section(PLATFORM_SECTION_C) - .unwrap() - .data - .clone(), - ); - let player_x = Pubkey::new(&[0xA; 32]); - let player_y = Pubkey::new(&[0xB; 32]); - - let ttt1 = TicTacToe::new(&loader, &ttt_program); - ttt1.init(&loader, &ttt_program, &player_x); - ttt1.command(&loader, &ttt_program, Command::Join(0xAABBCCDD), &player_y); - ttt1.command(&loader, &ttt_program, Command::Move(1, 1), &player_x); - ttt1.command(&loader, &ttt_program, Command::Move(0, 0), &player_y); - ttt1.command(&loader, &ttt_program, Command::Move(2, 0), &player_x); - ttt1.command(&loader, &ttt_program, Command::Move(0, 2), &player_y); - ttt1.command(&loader, &ttt_program, Command::Move(2, 2), &player_x); - ttt1.command(&loader, &ttt_program, Command::Move(0, 1), &player_y); - - let ttt2 = TicTacToe::new(&loader, &ttt_program); - ttt2.init(&loader, &ttt_program, &player_x); - ttt2.command(&loader, &ttt_program, Command::Join(0xAABBCCDD), &player_y); - ttt2.command(&loader, &ttt_program, Command::Move(1, 1), &player_x); - ttt2.command(&loader, &ttt_program, Command::Move(0, 0), &player_y); - ttt2.command(&loader, &ttt_program, Command::Move(2, 0), &player_x); - ttt2.command(&loader, &ttt_program, Command::Move(0, 2), &player_y); - ttt2.command(&loader, &ttt_program, Command::Move(2, 2), &player_x); - ttt2.command(&loader, &ttt_program, Command::Move(0, 1), &player_y); - - let ttt3 = TicTacToe::new(&loader, &ttt_program); - ttt3.init(&loader, &ttt_program, &player_x); - - let dashboard_program = Program::new( - &loader, - elf::File::open_path(&create_bpf_path("tictactoe_dashboard")) - .unwrap() - .get_section(PLATFORM_SECTION_C) - .unwrap() - .data - .clone(), - ); - let dashboard = Dashboard::new(&loader, &dashboard_program); - - dashboard.update(&loader, &dashboard_program, &ttt1.id()); - dashboard.update(&loader, &dashboard_program, &ttt2.id()); - dashboard.update(&loader, &dashboard_program, &ttt3.id()); - - assert_eq!(ttt1.id().as_ref(), &dashboard.get_game(&loader, 1)[..]); - assert_eq!(ttt2.id().as_ref(), &dashboard.get_game(&loader, 0)[..]); - assert_eq!(ttt3.id().as_ref(), &dashboard.get_pending(&loader)[..]); -}