coin_toss.aes
1 include "String.aes" 2 3 contract CoinToss = 4 record state = { player : address, 5 casino : address, 6 hash : option(hash), 7 height : int, 8 casino_pick : option(string), 9 stake : int, 10 reaction_time : int 11 } 12 13 entrypoint init(player: address, casino: address, reaction_time: int) : state = 14 require(Call.value == 0, "no_deposit") 15 { player = player, 16 casino = casino, 17 hash = None, 18 height = 0, 19 casino_pick = None, 20 stake = 0, 21 reaction_time = reaction_time 22 } 23 24 payable stateful entrypoint provide_hash(hash: hash) = 25 require_player() 26 require(state.hash == None, "already_has_hash") 27 put(state{ hash = Some(hash), 28 stake = Call.value, 29 height = Chain.block_height}) 30 31 payable stateful entrypoint casino_pick(coin_side: string) = 32 require_casino() 33 ensure_casino_turn_to_pick() 34 ensure_coin_side(coin_side) 35 require(Call.value == state.stake, 36 String.concat("wrong_stake, expected ", Int.to_str(state.stake))) 37 put(state{ casino_pick = Some(coin_side), 38 height = Chain.block_height}) 39 40 stateful entrypoint reveal(key: string, coin_side: string) = 41 require_player() 42 ensure_player_turn_to_reveal() 43 ensure_coin_side(coin_side) 44 ensure_if_key_is_valid(key, coin_side) 45 let Some(casino_pick) = state.casino_pick 46 let winner : address = 47 if (coin_side == casino_pick) 48 state.casino 49 else 50 state.player 51 Chain.spend(winner, Contract.balance) 52 reset_state() 53 winner 54 55 stateful entrypoint casino_dispute_no_reveal() = 56 require_casino() 57 ensure_player_turn_to_reveal() 58 require(state.height + state.reaction_time < Chain.block_height, "not_yet_allowed") 59 Chain.spend(state.casino, Contract.balance) 60 reset_state() 61 62 stateful entrypoint player_dispute_no_pick() = 63 require_player() 64 ensure_casino_turn_to_pick() 65 require(state.height + state.reaction_time < Chain.block_height, "not_yet_allowed") 66 Chain.spend(state.player, Contract.balance) 67 reset_state() 68 69 // a friendly helper function 70 entrypoint compute_hash(key: string, coin_side: string) : hash = 71 ensure_coin_side(coin_side) 72 String.sha256(String.concat(key, coin_side)) 73 74 // internal functions 75 76 function ensure_coin_side(coin_side: string) = 77 require(coin_side == "heads" || coin_side == "tails", "invalid_coin_side") 78 79 function ensure_casino_turn_to_pick() = 80 require(state.hash != None, "no_hash") 81 require(state.casino_pick == None, "there_is_a_pick_already") 82 83 function ensure_player_turn_to_reveal() = 84 require(state.casino_pick != None, "there_is_no_pick") 85 86 function require_casino() = 87 require(Call.caller == state.casino, "not_casino") 88 89 function require_player() = 90 require(Call.caller == state.player, "not_player") 91 92 function ensure_if_key_is_valid(key: string, coin_side: string) = 93 let computed_hash = compute_hash(key, coin_side) 94 let Some(stored_hash) = state.hash 95 require(stored_hash == computed_hash, "invalid_key_and_answer") 96 97 stateful function reset_state() = 98 put(state{hash = None, 99 height = 0, 100 casino_pick = None, 101 stake = 0 102 })