Transaction Hash:
Block:
6575864 at Oct-24-2018 04:54:52 PM +UTC
Transaction Fee:
0.00064728 ETH
$1.58
Gas Used:
80,910 Gas / 8 Gwei
Emitted Events:
43 |
GameChannel.LogGameCreated( user=[Sender] 0x89a548195582228eb1267908d7758b507b1ab847, gameId=1743, stake=10000000000000000, serverEndHash=6E62867B40952DB6CE95F8FE5118C2683D5A2BE281A6CFEF75DC18F853F8F6D7, userEndHash=AF6894EE48D86460B06DA82F55C4D7ED2E331BABF0EDE266E1867874EA1FF52E )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x52bc44d5...b7d7bE3b5
Miner
| (Nanopool) | 8,374.393331579298685347 Eth | 8,374.393978859298685347 Eth | 0.00064728 | |
0x89a54819...07b1ab847 |
0.012996908318332754 Eth
Nonce: 57
|
0.002349628318332754 Eth
Nonce: 58
| 0.01064728 | ||
0xEB6F4eC3...3e271dF3b | 17.734997815 Eth | 17.744997815 Eth | 0.01 |
Execution Trace
ETH 0.01
GameChannel.createGame( _userEndHash=AF6894EE48D86460B06DA82F55C4D7ED2E331BABF0EDE266E1867874EA1FF52E, _previousGameId=1687, _createBefore=1540421687, _serverEndHash=6E62867B40952DB6CE95F8FE5118C2683D5A2BE281A6CFEF75DC18F853F8F6D7, _serverSig=0xB2CD2DD7D69187F132D40C9D77DBADE8CDF9B441A3999BC4409049F979EAE70E0F28B6683C7EFA19018AD7B562985C1477EEB7A9184C7C15288BCE7F635011E51B )
-
ConflictResolution.minHouseStake( activeGames=2 ) => ( 15000000000000000000 )
-
Null: 0x000...001.2ad38e3f( )
createGame[GameChannel (ln:1186)]
verifyCreateSig[GameChannel (ln:1206)]
verify[GameChannel (ln:1328)]
add[GameChannel (ln:1215)]
LogGameCreated[GameChannel (ln:1218)]
File 1 of 2: GameChannel
File 2 of 2: ConflictResolution
pragma solidity ^0.4.24; interface ConflictResolutionInterface { function minHouseStake(uint activeGames) external pure returns(uint); function maxBalance() external pure returns(int); function conflictEndFine() external pure returns(int); function isValidBet(uint8 _gameType, uint _betNum, uint _betValue) external pure returns(bool); function endGameConflict( uint8 _gameType, uint _betNum, uint _betValue, int _balance, uint _stake, bytes32 _serverSeed, bytes32 _userSeed ) external view returns(int); function serverForceGameEnd( uint8 gameType, uint _betNum, uint _betValue, int _balance, uint _stake, uint _endInitiatedTime ) external view returns(int); function userForceGameEnd( uint8 _gameType, uint _betNum, uint _betValue, int _balance, uint _stake, uint _endInitiatedTime ) external view returns(int); } library MathUtil { /** * @dev Returns the absolute value of _val. * @param _val value * @return The absolute value of _val. */ function abs(int _val) internal pure returns(uint) { if (_val < 0) { return uint(-_val); } else { return uint(_val); } } /** * @dev Calculate maximum. */ function max(uint _val1, uint _val2) internal pure returns(uint) { return _val1 >= _val2 ? _val1 : _val2; } /** * @dev Calculate minimum. */ function min(uint _val1, uint _val2) internal pure returns(uint) { return _val1 <= _val2 ? _val1 : _val2; } } contract Ownable { address public owner; address public pendingOwner; event LogOwnerShipTransferred(address indexed previousOwner, address indexed newOwner); event LogOwnerShipTransferInitiated(address indexed previousOwner, address indexed newOwner); /** * @dev Modifier, which throws if called by other account than owner. */ modifier onlyOwner { require(msg.sender == owner); _; } /** * @dev Modifier throws if called by any account other than the pendingOwner. */ modifier onlyPendingOwner() { require(msg.sender == pendingOwner); _; } /** * @dev Set contract creator as initial owner */ constructor() public { owner = msg.sender; pendingOwner = address(0); } /** * @dev Allows the current owner to set the pendingOwner address. * @param _newOwner The address to transfer ownership to. */ function transferOwnership(address _newOwner) public onlyOwner { pendingOwner = _newOwner; emit LogOwnerShipTransferInitiated(owner, _newOwner); } /** * @dev PendingOwner can accept ownership. */ function claimOwnership() public onlyPendingOwner { owner = pendingOwner; pendingOwner = address(0); emit LogOwnerShipTransferred(owner, pendingOwner); } } contract Activatable is Ownable { bool public activated = false; /// @dev Event is fired if activated. event LogActive(); /// @dev Modifier, which only allows function execution if activated. modifier onlyActivated() { require(activated); _; } /// @dev Modifier, which only allows function execution if not activated. modifier onlyNotActivated() { require(!activated); _; } /// @dev activate contract, can be only called once by the contract owner. function activate() public onlyOwner onlyNotActivated { activated = true; emit LogActive(); } } contract ConflictResolutionManager is Ownable { /// @dev Conflict resolution contract. ConflictResolutionInterface public conflictRes; /// @dev New Conflict resolution contract. address public newConflictRes = 0; /// @dev Time update of new conflict resolution contract was initiated. uint public updateTime = 0; /// @dev Min time before new conflict res contract can be activated after initiating update. uint public constant MIN_TIMEOUT = 3 days; /// @dev Min time before new conflict res contract can be activated after initiating update. uint public constant MAX_TIMEOUT = 6 days; /// @dev Update of conflict resolution contract was initiated. event LogUpdatingConflictResolution(address newConflictResolutionAddress); /// @dev New conflict resolution contract is active. event LogUpdatedConflictResolution(address newConflictResolutionAddress); /** * @dev Constructor * @param _conflictResAddress conflict resolution contract address. */ constructor(address _conflictResAddress) public { conflictRes = ConflictResolutionInterface(_conflictResAddress); } /** * @dev Initiate conflict resolution contract update. * @param _newConflictResAddress New conflict resolution contract address. */ function updateConflictResolution(address _newConflictResAddress) public onlyOwner { newConflictRes = _newConflictResAddress; updateTime = block.timestamp; emit LogUpdatingConflictResolution(_newConflictResAddress); } /** * @dev Active new conflict resolution contract. */ function activateConflictResolution() public onlyOwner { require(newConflictRes != 0); require(updateTime != 0); require(updateTime + MIN_TIMEOUT <= block.timestamp && block.timestamp <= updateTime + MAX_TIMEOUT); conflictRes = ConflictResolutionInterface(newConflictRes); newConflictRes = 0; updateTime = 0; emit LogUpdatedConflictResolution(newConflictRes); } } contract Pausable is Activatable { using SafeMath for uint; /// @dev Is contract paused. Initial it is paused. bool public paused = true; /// @dev Time pause was called uint public timePaused = block.timestamp; /// @dev Modifier, which only allows function execution if not paused. modifier onlyNotPaused() { require(!paused, "paused"); _; } /// @dev Modifier, which only allows function execution if paused. modifier onlyPaused() { require(paused); _; } /// @dev Modifier, which only allows function execution if paused longer than timeSpan. modifier onlyPausedSince(uint timeSpan) { require(paused && (timePaused.add(timeSpan) <= block.timestamp)); _; } /// @dev Event is fired if paused. event LogPause(); /// @dev Event is fired if pause is ended. event LogUnpause(); /** * @dev Pause contract. No new game sessions can be created. */ function pause() public onlyOwner onlyNotPaused { paused = true; timePaused = block.timestamp; emit LogPause(); } /** * @dev Unpause contract. Initial contract is paused and can only be unpaused after activating it. */ function unpause() public onlyOwner onlyPaused onlyActivated { paused = false; timePaused = 0; emit LogUnpause(); } } contract Destroyable is Pausable { /// @dev After pausing the contract for 20 days owner can selfdestruct it. uint public constant TIMEOUT_DESTROY = 20 days; /** * @dev Destroy contract and transfer ether to owner. */ function destroy() public onlyOwner onlyPausedSince(TIMEOUT_DESTROY) { selfdestruct(owner); } } contract GameChannelBase is Destroyable, ConflictResolutionManager { using SafeCast for int; using SafeCast for uint; using SafeMath for int; using SafeMath for uint; /// @dev Different game session states. enum GameStatus { ENDED, ///< @dev Game session is ended. ACTIVE, ///< @dev Game session is active. USER_INITIATED_END, ///< @dev User initiated non regular end. SERVER_INITIATED_END ///< @dev Server initiated non regular end. } /// @dev Reason game session ended. enum ReasonEnded { REGULAR_ENDED, ///< @dev Game session is regularly ended. SERVER_FORCED_END, ///< @dev User did not respond. Server forced end. USER_FORCED_END, ///< @dev Server did not respond. User forced end. CONFLICT_ENDED ///< @dev Server or user raised conflict ans pushed game state, opponent pushed same game state. } struct Game { /// @dev Game session status. GameStatus status; /// @dev User's stake. uint128 stake; /// @dev Last game round info if not regularly ended. /// If game session is ended normally this data is not used. uint8 gameType; uint32 roundId; uint betNum; uint betValue; int balance; bytes32 userSeed; bytes32 serverSeed; uint endInitiatedTime; } /// @dev Minimal time span between profit transfer. uint public constant MIN_TRANSFER_TIMESPAN = 1 days; /// @dev Maximal time span between profit transfer. uint public constant MAX_TRANSFER_TIMSPAN = 6 * 30 days; bytes32 public constant TYPE_HASH = keccak256(abi.encodePacked( "uint32 Round Id", "uint8 Game Type", "uint16 Number", "uint Value (Wei)", "int Current Balance (Wei)", "bytes32 Server Hash", "bytes32 Player Hash", "uint Game Id", "address Contract Address" )); /// @dev Current active game sessions. uint public activeGames = 0; /// @dev Game session id counter. Points to next free game session slot. So gameIdCntr -1 is the // number of game sessions created. uint public gameIdCntr = 1; /// @dev Only this address can accept and end games. address public serverAddress; /// @dev Address to transfer profit to. address public houseAddress; /// @dev Current house stake. uint public houseStake = 0; /// @dev House profit since last profit transfer. int public houseProfit = 0; /// @dev Min value user needs to deposit for creating game session. uint128 public minStake; /// @dev Max value user can deposit for creating game session. uint128 public maxStake; /// @dev Timeout until next profit transfer is allowed. uint public profitTransferTimeSpan = 14 days; /// @dev Last time profit transferred to house. uint public lastProfitTransferTimestamp; /// @dev Maps gameId to game struct. mapping (uint => Game) public gameIdGame; /// @dev Maps user address to current user game id. mapping (address => uint) public userGameId; /// @dev Maps user address to pending returns. mapping (address => uint) public pendingReturns; /// @dev Modifier, which only allows to execute if house stake is high enough. modifier onlyValidHouseStake(uint _activeGames) { uint minHouseStake = conflictRes.minHouseStake(_activeGames); require(houseStake >= minHouseStake, "inv houseStake"); _; } /// @dev Modifier to check if value send fulfills user stake requirements. modifier onlyValidValue() { require(minStake <= msg.value && msg.value <= maxStake, "inv stake"); _; } /// @dev Modifier, which only allows server to call function. modifier onlyServer() { require(msg.sender == serverAddress); _; } /// @dev Modifier, which only allows to set valid transfer timeouts. modifier onlyValidTransferTimeSpan(uint transferTimeout) { require(transferTimeout >= MIN_TRANSFER_TIMESPAN && transferTimeout <= MAX_TRANSFER_TIMSPAN); _; } /// @dev This event is fired when user creates game session. event LogGameCreated(address indexed user, uint indexed gameId, uint128 stake, bytes32 indexed serverEndHash, bytes32 userEndHash); /// @dev This event is fired when user requests conflict end. event LogUserRequestedEnd(address indexed user, uint indexed gameId); /// @dev This event is fired when server requests conflict end. event LogServerRequestedEnd(address indexed user, uint indexed gameId); /// @dev This event is fired when game session is ended. event LogGameEnded(address indexed user, uint indexed gameId, uint32 roundId, int balance, ReasonEnded reason); /// @dev this event is fired when owner modifies user's stake limits. event LogStakeLimitsModified(uint minStake, uint maxStake); /** * @dev Contract constructor. * @param _serverAddress Server address. * @param _minStake Min value user needs to deposit to create game session. * @param _maxStake Max value user can deposit to create game session. * @param _conflictResAddress Conflict resolution contract address. * @param _houseAddress House address to move profit to. * @param _chainId Chain id for signature domain. */ constructor( address _serverAddress, uint128 _minStake, uint128 _maxStake, address _conflictResAddress, address _houseAddress, uint _chainId ) public ConflictResolutionManager(_conflictResAddress) { require(_minStake > 0 && _minStake <= _maxStake); serverAddress = _serverAddress; houseAddress = _houseAddress; lastProfitTransferTimestamp = block.timestamp; minStake = _minStake; maxStake = _maxStake; } /** * @dev Set gameIdCntr. Can be only set before activating contract. */ function setGameIdCntr(uint _gameIdCntr) public onlyOwner onlyNotActivated { require(gameIdCntr > 0); gameIdCntr = _gameIdCntr; } /** * @notice Withdraw pending returns. */ function withdraw() public { uint toTransfer = pendingReturns[msg.sender]; require(toTransfer > 0); pendingReturns[msg.sender] = 0; msg.sender.transfer(toTransfer); } /** * @notice Transfer house profit to houseAddress. */ function transferProfitToHouse() public { require(lastProfitTransferTimestamp.add(profitTransferTimeSpan) <= block.timestamp); // update last transfer timestamp lastProfitTransferTimestamp = block.timestamp; if (houseProfit <= 0) { // no profit to transfer return; } uint toTransfer = houseProfit.castToUint(); houseProfit = 0; houseStake = houseStake.sub(toTransfer); houseAddress.transfer(toTransfer); } /** * @dev Set profit transfer time span. */ function setProfitTransferTimeSpan(uint _profitTransferTimeSpan) public onlyOwner onlyValidTransferTimeSpan(_profitTransferTimeSpan) { profitTransferTimeSpan = _profitTransferTimeSpan; } /** * @dev Increase house stake by msg.value */ function addHouseStake() public payable onlyOwner { houseStake = houseStake.add(msg.value); } /** * @dev Withdraw house stake. */ function withdrawHouseStake(uint value) public onlyOwner { uint minHouseStake = conflictRes.minHouseStake(activeGames); require(value <= houseStake && houseStake.sub(value) >= minHouseStake); require(houseProfit <= 0 || houseProfit.castToUint() <= houseStake.sub(value)); houseStake = houseStake.sub(value); owner.transfer(value); } /** * @dev Withdraw house stake and profit. */ function withdrawAll() public onlyOwner onlyPausedSince(3 days) { houseProfit = 0; uint toTransfer = houseStake; houseStake = 0; owner.transfer(toTransfer); } /** * @dev Set new house address. * @param _houseAddress New house address. */ function setHouseAddress(address _houseAddress) public onlyOwner { houseAddress = _houseAddress; } /** * @dev Set stake min and max value. * @param _minStake Min stake. * @param _maxStake Max stake. */ function setStakeRequirements(uint128 _minStake, uint128 _maxStake) public onlyOwner { require(_minStake > 0 && _minStake <= _maxStake); minStake = _minStake; maxStake = _maxStake; emit LogStakeLimitsModified(minStake, maxStake); } /** * @dev Close game session. * @param _game Game session data. * @param _gameId Id of game session. * @param _userAddress User's address of game session. * @param _reason Reason for closing game session. * @param _balance Game session balance. */ function closeGame( Game storage _game, uint _gameId, uint32 _roundId, address _userAddress, ReasonEnded _reason, int _balance ) internal { _game.status = GameStatus.ENDED; activeGames = activeGames.sub(1); payOut(_userAddress, _game.stake, _balance); emit LogGameEnded(_userAddress, _gameId, _roundId, _balance, _reason); } /** * @dev End game by paying out user and server. * @param _userAddress User's address. * @param _stake User's stake. * @param _balance User's balance. */ function payOut(address _userAddress, uint128 _stake, int _balance) internal { int stakeInt = _stake; int houseStakeInt = houseStake.castToInt(); assert(_balance <= conflictRes.maxBalance()); assert((stakeInt.add(_balance)) >= 0); if (_balance > 0 && houseStakeInt < _balance) { // Should never happen! // House is bankrupt. // Payout left money. _balance = houseStakeInt; } houseProfit = houseProfit.sub(_balance); int newHouseStake = houseStakeInt.sub(_balance); houseStake = newHouseStake.castToUint(); uint valueUser = stakeInt.add(_balance).castToUint(); pendingReturns[_userAddress] += valueUser; if (pendingReturns[_userAddress] > 0) { safeSend(_userAddress); } } /** * @dev Send value of pendingReturns[_address] to _address. * @param _address Address to send value to. */ function safeSend(address _address) internal { uint valueToSend = pendingReturns[_address]; assert(valueToSend > 0); pendingReturns[_address] = 0; if (_address.send(valueToSend) == false) { pendingReturns[_address] = valueToSend; } } /** * @dev Verify signature of given data. Throws on verification failure. * @param _sig Signature of given data in the form of rsv. * @param _address Address of signature signer. */ function verifySig( uint32 _roundId, uint8 _gameType, uint _num, uint _value, int _balance, bytes32 _serverHash, bytes32 _userHash, uint _gameId, address _contractAddress, bytes _sig, address _address ) internal view { // check if this is the correct contract address contractAddress = this; require(_contractAddress == contractAddress, "inv contractAddress"); bytes32 roundHash = calcHash( _roundId, _gameType, _num, _value, _balance, _serverHash, _userHash, _gameId ); verify( roundHash, _sig, _address ); } /** * @dev Check if _sig is valid signature of _hash. Throws if invalid signature. * @param _hash Hash to check signature of. * @param _sig Signature of _hash. * @param _address Address of signer. */ function verify( bytes32 _hash, bytes _sig, address _address ) internal pure { (bytes32 r, bytes32 s, uint8 v) = signatureSplit(_sig); address addressRecover = ecrecover(_hash, v, r, s); require(addressRecover == _address, "inv sig"); } /** * @dev Calculate typed hash of given data (compare eth_signTypedData). * @return Hash of given data. */ function calcHash( uint32 _roundId, uint8 _gameType, uint _num, uint _value, int _balance, bytes32 _serverHash, bytes32 _userHash, uint _gameId ) private view returns(bytes32) { bytes32 betHash = keccak256(abi.encodePacked( _roundId, _gameType, uint16(_num), _value, _balance, _serverHash, _userHash, _gameId, address(this) )); return keccak256(abi.encodePacked( TYPE_HASH, betHash )); } /** * @dev Split the given signature of the form rsv in r s v. v is incremented with 27 if * it is below 2. * @param _signature Signature to split. * @return r s v */ function signatureSplit(bytes _signature) private pure returns (bytes32 r, bytes32 s, uint8 v) { require(_signature.length == 65, "inv sig"); assembly { r := mload(add(_signature, 32)) s := mload(add(_signature, 64)) v := and(mload(add(_signature, 65)), 0xff) } if (v < 2) { v = v + 27; } } } contract GameChannelConflict is GameChannelBase { using SafeCast for int; using SafeCast for uint; using SafeMath for int; using SafeMath for uint; /** * @dev Contract constructor. * @param _serverAddress Server address. * @param _minStake Min value user needs to deposit to create game session. * @param _maxStake Max value user can deposit to create game session. * @param _conflictResAddress Conflict resolution contract address * @param _houseAddress House address to move profit to * @param _chainId Chain id for signature domain. */ constructor( address _serverAddress, uint128 _minStake, uint128 _maxStake, address _conflictResAddress, address _houseAddress, uint _chainId ) public GameChannelBase(_serverAddress, _minStake, _maxStake, _conflictResAddress, _houseAddress, _chainId) { // nothing to do } /** * @dev Used by server if user does not end game session. * @param _roundId Round id of bet. * @param _gameType Game type of bet. * @param _num Number of bet. * @param _value Value of bet. * @param _balance Balance before this bet. * @param _serverHash Hash of server seed for this bet. * @param _userHash Hash of user seed for this bet. * @param _gameId Game session id. * @param _contractAddress Address of this contract. * @param _userSig User signature of this bet. * @param _userAddress Address of user. * @param _serverSeed Server seed for this bet. * @param _userSeed User seed for this bet. */ function serverEndGameConflict( uint32 _roundId, uint8 _gameType, uint _num, uint _value, int _balance, bytes32 _serverHash, bytes32 _userHash, uint _gameId, address _contractAddress, bytes _userSig, address _userAddress, bytes32 _serverSeed, bytes32 _userSeed ) public onlyServer { verifySig( _roundId, _gameType, _num, _value, _balance, _serverHash, _userHash, _gameId, _contractAddress, _userSig, _userAddress ); serverEndGameConflictImpl( _roundId, _gameType, _num, _value, _balance, _serverHash, _userHash, _serverSeed, _userSeed, _gameId, _userAddress ); } /** * @notice Can be used by user if server does not answer to the end game session request. * @param _roundId Round id of bet. * @param _gameType Game type of bet. * @param _num Number of bet. * @param _value Value of bet. * @param _balance Balance before this bet. * @param _serverHash Hash of server seed for this bet. * @param _userHash Hash of user seed for this bet. * @param _gameId Game session id. * @param _contractAddress Address of this contract. * @param _serverSig Server signature of this bet. * @param _userSeed User seed for this bet. */ function userEndGameConflict( uint32 _roundId, uint8 _gameType, uint _num, uint _value, int _balance, bytes32 _serverHash, bytes32 _userHash, uint _gameId, address _contractAddress, bytes _serverSig, bytes32 _userSeed ) public { verifySig( _roundId, _gameType, _num, _value, _balance, _serverHash, _userHash, _gameId, _contractAddress, _serverSig, serverAddress ); userEndGameConflictImpl( _roundId, _gameType, _num, _value, _balance, _userHash, _userSeed, _gameId, msg.sender ); } /** * @notice Cancel active game without playing. Useful if server stops responding before * one game is played. * @param _gameId Game session id. */ function userCancelActiveGame(uint _gameId) public { address userAddress = msg.sender; uint gameId = userGameId[userAddress]; Game storage game = gameIdGame[gameId]; require(gameId == _gameId, "inv gameId"); if (game.status == GameStatus.ACTIVE) { game.endInitiatedTime = block.timestamp; game.status = GameStatus.USER_INITIATED_END; emit LogUserRequestedEnd(msg.sender, gameId); } else if (game.status == GameStatus.SERVER_INITIATED_END && game.roundId == 0) { cancelActiveGame(game, gameId, userAddress); } else { revert(); } } /** * @dev Cancel active game without playing. Useful if user starts game session and * does not play. * @param _userAddress Users' address. * @param _gameId Game session id. */ function serverCancelActiveGame(address _userAddress, uint _gameId) public onlyServer { uint gameId = userGameId[_userAddress]; Game storage game = gameIdGame[gameId]; require(gameId == _gameId, "inv gameId"); if (game.status == GameStatus.ACTIVE) { game.endInitiatedTime = block.timestamp; game.status = GameStatus.SERVER_INITIATED_END; emit LogServerRequestedEnd(msg.sender, gameId); } else if (game.status == GameStatus.USER_INITIATED_END && game.roundId == 0) { cancelActiveGame(game, gameId, _userAddress); } else { revert(); } } /** * @dev Force end of game if user does not respond. Only possible after a certain period of time * to give the user a chance to respond. * @param _userAddress User's address. */ function serverForceGameEnd(address _userAddress, uint _gameId) public onlyServer { uint gameId = userGameId[_userAddress]; Game storage game = gameIdGame[gameId]; require(gameId == _gameId, "inv gameId"); require(game.status == GameStatus.SERVER_INITIATED_END, "inv status"); // theoretically we have enough data to calculate winner // but as user did not respond assume he has lost. int newBalance = conflictRes.serverForceGameEnd( game.gameType, game.betNum, game.betValue, game.balance, game.stake, game.endInitiatedTime ); closeGame(game, gameId, game.roundId, _userAddress, ReasonEnded.SERVER_FORCED_END, newBalance); } /** * @notice Force end of game if server does not respond. Only possible after a certain period of time * to give the server a chance to respond. */ function userForceGameEnd(uint _gameId) public { address userAddress = msg.sender; uint gameId = userGameId[userAddress]; Game storage game = gameIdGame[gameId]; require(gameId == _gameId, "inv gameId"); require(game.status == GameStatus.USER_INITIATED_END, "inv status"); int newBalance = conflictRes.userForceGameEnd( game.gameType, game.betNum, game.betValue, game.balance, game.stake, game.endInitiatedTime ); closeGame(game, gameId, game.roundId, userAddress, ReasonEnded.USER_FORCED_END, newBalance); } /** * @dev Conflict handling implementation. Stores game data and timestamp if game * is active. If server has already marked conflict for game session the conflict * resolution contract is used (compare conflictRes). * @param _roundId Round id of bet. * @param _gameType Game type of bet. * @param _num Number of bet. * @param _value Value of bet. * @param _balance Balance before this bet. * @param _userHash Hash of user's seed for this bet. * @param _userSeed User's seed for this bet. * @param _gameId game Game session id. * @param _userAddress User's address. */ function userEndGameConflictImpl( uint32 _roundId, uint8 _gameType, uint _num, uint _value, int _balance, bytes32 _userHash, bytes32 _userSeed, uint _gameId, address _userAddress ) private { uint gameId = userGameId[_userAddress]; Game storage game = gameIdGame[gameId]; int maxBalance = conflictRes.maxBalance(); int gameStake = game.stake; require(gameId == _gameId, "inv gameId"); require(_roundId > 0, "inv roundId"); require(keccak256(abi.encodePacked(_userSeed)) == _userHash, "inv userSeed"); require(-gameStake <= _balance && _balance <= maxBalance, "inv balance"); // game.stake save to cast as uint128 require(conflictRes.isValidBet(_gameType, _num, _value), "inv bet"); require(gameStake.add(_balance).sub(_value.castToInt()) >= 0, "value too high"); // game.stake save to cast as uint128 if (game.status == GameStatus.SERVER_INITIATED_END && game.roundId == _roundId) { game.userSeed = _userSeed; endGameConflict(game, gameId, _userAddress); } else if (game.status == GameStatus.ACTIVE || (game.status == GameStatus.SERVER_INITIATED_END && game.roundId < _roundId)) { game.status = GameStatus.USER_INITIATED_END; game.endInitiatedTime = block.timestamp; game.roundId = _roundId; game.gameType = _gameType; game.betNum = _num; game.betValue = _value; game.balance = _balance; game.userSeed = _userSeed; game.serverSeed = bytes32(0); emit LogUserRequestedEnd(msg.sender, gameId); } else { revert("inv state"); } } /** * @dev Conflict handling implementation. Stores game data and timestamp if game * is active. If user has already marked conflict for game session the conflict * resolution contract is used (compare conflictRes). * @param _roundId Round id of bet. * @param _gameType Game type of bet. * @param _num Number of bet. * @param _value Value of bet. * @param _balance Balance before this bet. * @param _serverHash Hash of server's seed for this bet. * @param _userHash Hash of user's seed for this bet. * @param _serverSeed Server's seed for this bet. * @param _userSeed User's seed for this bet. * @param _userAddress User's address. */ function serverEndGameConflictImpl( uint32 _roundId, uint8 _gameType, uint _num, uint _value, int _balance, bytes32 _serverHash, bytes32 _userHash, bytes32 _serverSeed, bytes32 _userSeed, uint _gameId, address _userAddress ) private { uint gameId = userGameId[_userAddress]; Game storage game = gameIdGame[gameId]; int maxBalance = conflictRes.maxBalance(); int gameStake = game.stake; require(gameId == _gameId, "inv gameId"); require(_roundId > 0, "inv roundId"); require(keccak256(abi.encodePacked(_serverSeed)) == _serverHash, "inv serverSeed"); require(keccak256(abi.encodePacked(_userSeed)) == _userHash, "inv userSeed"); require(-gameStake <= _balance && _balance <= maxBalance, "inv balance"); // game.stake save to cast as uint128 require(conflictRes.isValidBet(_gameType, _num, _value), "inv bet"); require(gameStake.add(_balance).sub(_value.castToInt()) >= 0, "too high value"); // game.stake save to cast as uin128 if (game.status == GameStatus.USER_INITIATED_END && game.roundId == _roundId) { game.serverSeed = _serverSeed; endGameConflict(game, gameId, _userAddress); } else if (game.status == GameStatus.ACTIVE || (game.status == GameStatus.USER_INITIATED_END && game.roundId < _roundId)) { game.status = GameStatus.SERVER_INITIATED_END; game.endInitiatedTime = block.timestamp; game.roundId = _roundId; game.gameType = _gameType; game.betNum = _num; game.betValue = _value; game.balance = _balance; game.serverSeed = _serverSeed; game.userSeed = _userSeed; emit LogServerRequestedEnd(_userAddress, gameId); } else { revert("inv state"); } } /** * @dev End conflicting game without placed bets. * @param _game Game session data. * @param _gameId Game session id. * @param _userAddress User's address. */ function cancelActiveGame(Game storage _game, uint _gameId, address _userAddress) private { // user need to pay a fee when conflict ended. // this ensures a malicious, rich user can not just generate game sessions and then wait // for us to end the game session and then confirm the session status, so // we would have to pay a high gas fee without profit. int newBalance = -conflictRes.conflictEndFine(); // do not allow balance below user stake int stake = _game.stake; if (newBalance < -stake) { newBalance = -stake; } closeGame(_game, _gameId, 0, _userAddress, ReasonEnded.CONFLICT_ENDED, newBalance); } /** * @dev End conflicting game. * @param _game Game session data. * @param _gameId Game session id. * @param _userAddress User's address. */ function endGameConflict(Game storage _game, uint _gameId, address _userAddress) private { int newBalance = conflictRes.endGameConflict( _game.gameType, _game.betNum, _game.betValue, _game.balance, _game.stake, _game.serverSeed, _game.userSeed ); closeGame(_game, _gameId, _game.roundId, _userAddress, ReasonEnded.CONFLICT_ENDED, newBalance); } } contract GameChannel is GameChannelConflict { /** * @dev contract constructor * @param _serverAddress Server address. * @param _minStake Min value user needs to deposit to create game session. * @param _maxStake Max value user can deposit to create game session. * @param _conflictResAddress Conflict resolution contract address. * @param _houseAddress House address to move profit to. * @param _chainId Chain id for signature domain. */ constructor( address _serverAddress, uint128 _minStake, uint128 _maxStake, address _conflictResAddress, address _houseAddress, uint _chainId ) public GameChannelConflict(_serverAddress, _minStake, _maxStake, _conflictResAddress, _houseAddress, _chainId) { // nothing to do } /** * @notice Create games session request. msg.value needs to be valid stake value. * @param _userEndHash Last entry of users' hash chain. * @param _previousGameId User's previous game id, initial 0. * @param _createBefore Game can be only created before this timestamp. * @param _serverEndHash Last entry of server's hash chain. * @param _serverSig Server signature. See verifyCreateSig */ function createGame( bytes32 _userEndHash, uint _previousGameId, uint _createBefore, bytes32 _serverEndHash, bytes _serverSig ) public payable onlyValidValue onlyValidHouseStake(activeGames + 1) onlyNotPaused { uint previousGameId = userGameId[msg.sender]; Game storage game = gameIdGame[previousGameId]; require(game.status == GameStatus.ENDED, "prev game not ended"); require(previousGameId == _previousGameId, "inv gamePrevGameId"); require(block.timestamp < _createBefore, "expired"); verifyCreateSig(msg.sender, _previousGameId, _createBefore, _serverEndHash, _serverSig); uint gameId = gameIdCntr++; userGameId[msg.sender] = gameId; Game storage newGame = gameIdGame[gameId]; newGame.stake = uint128(msg.value); // It's safe to cast msg.value as it is limited, see onlyValidValue newGame.status = GameStatus.ACTIVE; activeGames = activeGames.add(1); // It's safe to cast msg.value as it is limited, see onlyValidValue emit LogGameCreated(msg.sender, gameId, uint128(msg.value), _serverEndHash, _userEndHash); } /** * @dev Regular end game session. Used if user and house have both * accepted current game session state. * The game session with gameId _gameId is closed * and the user paid out. This functions is called by the server after * the user requested the termination of the current game session. * @param _roundId Round id of bet. * @param _balance Current balance. * @param _serverHash Hash of server's seed for this bet. * @param _userHash Hash of user's seed for this bet. * @param _gameId Game session id. * @param _contractAddress Address of this contract. * @param _userAddress Address of user. * @param _userSig User's signature of this bet. */ function serverEndGame( uint32 _roundId, int _balance, bytes32 _serverHash, bytes32 _userHash, uint _gameId, address _contractAddress, address _userAddress, bytes _userSig ) public onlyServer { verifySig( _roundId, 0, 0, 0, _balance, _serverHash, _userHash, _gameId, _contractAddress, _userSig, _userAddress ); regularEndGame(_userAddress, _roundId, _balance, _gameId, _contractAddress); } /** * @notice Regular end game session. Normally not needed as server ends game (@see serverEndGame). * Can be used by user if server does not end game session. * @param _roundId Round id of bet. * @param _balance Current balance. * @param _serverHash Hash of server's seed for this bet. * @param _userHash Hash of user's seed for this bet. * @param _gameId Game session id. * @param _contractAddress Address of this contract. * @param _serverSig Server's signature of this bet. */ function userEndGame( uint32 _roundId, int _balance, bytes32 _serverHash, bytes32 _userHash, uint _gameId, address _contractAddress, bytes _serverSig ) public { verifySig( _roundId, 0, 0, 0, _balance, _serverHash, _userHash, _gameId, _contractAddress, _serverSig, serverAddress ); regularEndGame(msg.sender, _roundId, _balance, _gameId, _contractAddress); } /** * @dev Verify server signature. * @param _userAddress User's address. * @param _previousGameId User's previous game id, initial 0. * @param _createBefore Game can be only created before this timestamp. * @param _serverEndHash Last entry of server's hash chain. * @param _serverSig Server signature. */ function verifyCreateSig( address _userAddress, uint _previousGameId, uint _createBefore, bytes32 _serverEndHash, bytes _serverSig ) private view { address contractAddress = this; bytes32 hash = keccak256(abi.encodePacked( contractAddress, _userAddress, _previousGameId, _createBefore, _serverEndHash )); verify(hash, _serverSig, serverAddress); } /** * @dev Regular end game session implementation. Used if user and house have both * accepted current game session state. The game session with gameId _gameId is closed * and the user paid out. * @param _userAddress Address of user. * @param _balance Current balance. * @param _gameId Game session id. * @param _contractAddress Address of this contract. */ function regularEndGame( address _userAddress, uint32 _roundId, int _balance, uint _gameId, address _contractAddress ) private { uint gameId = userGameId[_userAddress]; Game storage game = gameIdGame[gameId]; int maxBalance = conflictRes.maxBalance(); int gameStake = game.stake; require(_gameId == gameId, "inv gameId"); require(_roundId > 0, "inv roundId"); // save to cast as game.stake hash fixed range require(-gameStake <= _balance && _balance <= maxBalance, "inv balance"); require(game.status == GameStatus.ACTIVE, "inv status"); assert(_contractAddress == address(this)); closeGame(game, gameId, _roundId, _userAddress, ReasonEnded.REGULAR_ENDED, _balance); } } library SafeCast { /** * Cast unsigned a to signed a. */ function castToInt(uint a) internal pure returns(int) { assert(a < (1 << 255)); return int(a); } /** * Cast signed a to unsigned a. */ function castToUint(int a) internal pure returns(uint) { assert(a >= 0); return uint(a); } } library SafeMath { /** * @dev Multiplies two unsigned integers, throws on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } c = a * b; assert(c / a == b); return c; } /** * @dev Multiplies two signed integers, throws on overflow. */ function mul(int256 a, int256 b) internal pure returns (int256) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } int256 c = a * b; assert(c / a == b); return c; } /** * @dev Integer division of two unsigned integers, truncating the quotient. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return a / b; } /** * @dev Integer division of two signed integers, truncating the quotient. */ function div(int256 a, int256 b) internal pure returns (int256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 // Overflow only happens when the smallest negative int is multiplied by -1. int256 INT256_MIN = int256((uint256(1) << 255)); assert(a != INT256_MIN || b != - 1); return a / b; } /** * @dev Subtracts two unsigned integers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } /** * @dev Subtracts two signed integers, throws on overflow. */ function sub(int256 a, int256 b) internal pure returns (int256) { int256 c = a - b; assert((b >= 0 && c <= a) || (b < 0 && c > a)); return c; } /** * @dev Adds two unsigned integers, throws on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256 c) { c = a + b; assert(c >= a); return c; } /** * @dev Adds two signed integers, throws on overflow. */ function add(int256 a, int256 b) internal pure returns (int256) { int256 c = a + b; assert((b >= 0 && c >= a) || (b < 0 && c < a)); return c; } }
File 2 of 2: ConflictResolution
pragma solidity ^0.4.24; interface ConflictResolutionInterface { function minHouseStake(uint activeGames) external pure returns(uint); function maxBalance() external pure returns(int); function conflictEndFine() external pure returns(int); function isValidBet(uint8 _gameType, uint _betNum, uint _betValue) external pure returns(bool); function endGameConflict( uint8 _gameType, uint _betNum, uint _betValue, int _balance, uint _stake, bytes32 _serverSeed, bytes32 _userSeed ) external view returns(int); function serverForceGameEnd( uint8 gameType, uint _betNum, uint _betValue, int _balance, uint _stake, uint _endInitiatedTime ) external view returns(int); function userForceGameEnd( uint8 _gameType, uint _betNum, uint _betValue, int _balance, uint _stake, uint _endInitiatedTime ) external view returns(int); } contract ConflictResolution is ConflictResolutionInterface { using SafeCast for int; using SafeCast for uint; using SafeMath for int; using SafeMath for uint; uint public constant DICE_RANGE = 100; uint public constant HOUSE_EDGE = 150; uint public constant HOUSE_EDGE_DIVISOR = 10000; uint public constant SERVER_TIMEOUT = 6 hours; uint public constant USER_TIMEOUT = 6 hours; uint8 public constant DICE_LOWER = 1; ///< @dev dice game lower number wins uint8 public constant DICE_HIGHER = 2; ///< @dev dice game higher number wins uint public constant MIN_BET_VALUE = 1e13; /// min 0.00001 ether bet uint public constant MIN_BANKROLL = 15e18; int public constant NOT_ENDED_FINE = 1e15; /// 0.001 ether int public constant CONFLICT_END_FINE = 1e15; /// 0.001 ether uint public constant PROBABILITY_DIVISOR = 10000; int public constant MAX_BALANCE = int(MIN_BANKROLL / 2); modifier onlyValidBet(uint8 _gameType, uint _betNum, uint _betValue) { require(isValidBet(_gameType, _betNum, _betValue), "inv bet"); _; } modifier onlyValidBalance(int _balance, uint _gameStake) { require(-_gameStake.castToInt() <= _balance && _balance <= MAX_BALANCE, "inv balance"); _; } /** * @dev Calc max bet we allow * We definitely do not allow bets greater than kelly criterion would allow. * => The max bet is limited to the max profit of houseEdge * bankroll. * => maxBet = houseEdge / (1/p * (1 - houseEdge) - 1) * bankroll, with p is win probability. * The max bet can be further restricted on backend. * @param _winProbability winProbability. * @return max allowed bet. */ function maxBet(uint _winProbability) public pure returns(uint) { assert(0 < _winProbability && _winProbability < PROBABILITY_DIVISOR); uint tmp1 = PROBABILITY_DIVISOR.mul(HOUSE_EDGE_DIVISOR).div(_winProbability); uint tmp2 = PROBABILITY_DIVISOR.mul(HOUSE_EDGE).div(_winProbability); uint enumerator = HOUSE_EDGE.mul(MIN_BANKROLL); uint denominator = tmp1.sub(tmp2).sub(HOUSE_EDGE_DIVISOR); uint maxBetVal = enumerator.div(denominator); return maxBetVal.add(5e14).div(1e15).mul(1e15); // round to multiple of 0.001 Ether } /** * @dev Check if bet is valid. * @param _gameType Game type. * @param _betNum Number of bet. * @param _betValue Value of bet. * @return True if bet is valid false otherwise. */ function isValidBet(uint8 _gameType, uint _betNum, uint _betValue) public pure returns(bool) { bool validMinBetValue = MIN_BET_VALUE <= _betValue; bool validGame = false; if (_gameType == DICE_LOWER) { validGame = _betNum > 0 && _betNum < DICE_RANGE - 1; validGame = validGame && _betValue <= maxBet(_betNum * PROBABILITY_DIVISOR / DICE_RANGE); } else if (_gameType == DICE_HIGHER) { validGame = _betNum > 0 && _betNum < DICE_RANGE - 1; validGame = validGame && _betValue <= maxBet((DICE_RANGE - _betNum - 1) * PROBABILITY_DIVISOR / DICE_RANGE); } else { validGame = false; } return validMinBetValue && validGame; } /** * @return Conflict end fine. */ function conflictEndFine() public pure returns(int) { return CONFLICT_END_FINE; } /** * @return Max balance. */ function maxBalance() public pure returns(int) { return MAX_BALANCE; } /** * Calculate minimum needed house stake. */ function minHouseStake(uint activeGames) public pure returns(uint) { return MathUtil.min(activeGames, 1) * MIN_BANKROLL; } /** * @dev Calculates game result and returns new balance. * @param _gameType Type of game. * @param _betNum Bet number. * @param _betValue Value of bet. * @param _balance Current balance. * @param _serverSeed Server's seed of current round. * @param _userSeed User's seed of current round. * @return New game session balance. */ function endGameConflict( uint8 _gameType, uint _betNum, uint _betValue, int _balance, uint _stake, bytes32 _serverSeed, bytes32 _userSeed ) public view onlyValidBet(_gameType, _betNum, _betValue) onlyValidBalance(_balance, _stake) returns(int) { require(_serverSeed != 0 && _userSeed != 0, "inv seeds"); int newBalance = processBet(_gameType, _betNum, _betValue, _balance, _serverSeed, _userSeed); // user need to pay a fee when conflict ended. // this ensures a malicious, rich user can not just generate game sessions and then wait // for us to end the game session and then confirm the session status, so // we would have to pay a high gas fee without profit. newBalance = newBalance.sub(CONFLICT_END_FINE); // do not allow balance below user stake int stake = _stake.castToInt(); if (newBalance < -stake) { newBalance = -stake; } return newBalance; } /** * @dev Force end of game if user does not respond. Only possible after a time period. * to give the user a chance to respond. * @param _gameType Game type. * @param _betNum Bet number. * @param _betValue Bet value. * @param _balance Current balance. * @param _stake User stake. * @param _endInitiatedTime Time server initiated end. * @return New game session balance. */ function serverForceGameEnd( uint8 _gameType, uint _betNum, uint _betValue, int _balance, uint _stake, uint _endInitiatedTime ) public view onlyValidBalance(_balance, _stake) returns(int) { require(_endInitiatedTime + SERVER_TIMEOUT <= block.timestamp, "too low timeout"); require(isValidBet(_gameType, _betNum, _betValue) || (_gameType == 0 && _betNum == 0 && _betValue == 0 && _balance == 0), "inv bet"); // assume user has lost int newBalance = _balance.sub(_betValue.castToInt()); // penalize user as he didn't end game newBalance = newBalance.sub(NOT_ENDED_FINE); // do not allow balance below user stake int stake = _stake.castToInt(); if (newBalance < -stake) { newBalance = -stake; } return newBalance; } /** * @dev Force end of game if server does not respond. Only possible after a time period * to give the server a chance to respond. * @param _gameType Game type. * @param _betNum Bet number. * @param _betValue Value of bet. * @param _balance Current balance. * @param _endInitiatedTime Time server initiated end. * @return New game session balance. */ function userForceGameEnd( uint8 _gameType, uint _betNum, uint _betValue, int _balance, uint _stake, uint _endInitiatedTime ) public view onlyValidBalance(_balance, _stake) returns(int) { require(_endInitiatedTime + USER_TIMEOUT <= block.timestamp, "too low timeout"); require(isValidBet(_gameType, _betNum, _betValue) || (_gameType == 0 && _betNum == 0 && _betValue == 0 && _balance == 0), "inv bet"); int profit = 0; if (_gameType == 0 && _betNum == 0 && _betValue == 0 && _balance == 0) { // user cancelled game without playing profit = 0; } else { profit = calculateProfit(_gameType, _betNum, _betValue); // safe to cast as ranges are limited } // penalize server as it didn't end game profit = profit.add(NOT_ENDED_FINE); return _balance.add(profit); } /** * @dev Calculate new balance after executing bet. * @param _gameType game type. * @param _betNum Bet Number. * @param _betValue Value of bet. * @param _balance Current balance. * @param _serverSeed Server's seed * @param _userSeed User's seed * return new balance. */ function processBet( uint8 _gameType, uint _betNum, uint _betValue, int _balance, bytes32 _serverSeed, bytes32 _userSeed ) public pure returns (int) { bool won = hasUserWon(_gameType, _betNum, _serverSeed, _userSeed); if (!won) { return _balance.sub(_betValue.castToInt()); } else { int profit = calculateProfit(_gameType, _betNum, _betValue); return _balance.add(profit); } } /** * @dev Calculate user profit. * @param _gameType type of game. * @param _betNum bet numbe. * @param _betValue bet value. * return profit of user */ function calculateProfit(uint8 _gameType, uint _betNum, uint _betValue) private pure returns(int) { uint betValueInGwei = _betValue / 1e9; // convert to gwei int res = 0; if (_gameType == DICE_LOWER) { res = calculateProfitGameType1(_betNum, betValueInGwei); } else if (_gameType == DICE_HIGHER) { res = calculateProfitGameType2(_betNum, betValueInGwei); } else { assert(false); } return res.mul(1e9); // convert to wei } /** * Calculate user profit from total won. * @param _totalWon user winning in gwei. * @return user profit in gwei. */ function calcProfitFromTotalWon(uint _totalWon, uint _betValue) private pure returns(int) { uint houseEdgeValue = _totalWon.mul(HOUSE_EDGE).div(HOUSE_EDGE_DIVISOR); return _totalWon.castToInt().sub(houseEdgeValue.castToInt()).sub(_betValue.castToInt()); } /** * @dev Calculate user profit if user has won for game type 1 (dice lower wins). * @param _betNum Bet number of user. * @param _betValue Value of bet in gwei. * @return Users' profit. */ function calculateProfitGameType1(uint _betNum, uint _betValue) private pure returns(int) { assert(_betNum > 0 && _betNum < DICE_RANGE); uint totalWon = _betValue.mul(DICE_RANGE).div(_betNum); return calcProfitFromTotalWon(totalWon, _betValue); } /** * @dev Calculate user profit if user has won for game type 2 (dice lower wins). * @param _betNum Bet number of user. * @param _betValue Value of bet in gwei. * @return Users' profit. */ function calculateProfitGameType2(uint _betNum, uint _betValue) private pure returns(int) { assert(_betNum >= 0 && _betNum < DICE_RANGE - 1); // safe as ranges are fixed uint totalWon = _betValue.mul(DICE_RANGE).div(DICE_RANGE.sub(_betNum).sub(1)); return calcProfitFromTotalWon(totalWon, _betValue); } /** * @dev Check if user hash won or lost. * @return true if user has won. */ function hasUserWon( uint8 _gameType, uint _betNum, bytes32 _serverSeed, bytes32 _userSeed ) public pure returns(bool) { bytes32 combinedHash = keccak256(abi.encodePacked(_serverSeed, _userSeed)); uint randNum = uint(combinedHash); if (_gameType == 1) { return calculateWinnerGameType1(randNum, _betNum); } else if (_gameType == 2) { return calculateWinnerGameType2(randNum, _betNum); } else { assert(false); } } /** * @dev Calculate winner of game type 1 (roll lower). * @param _randomNum 256 bit random number. * @param _betNum Bet number. * @return True if user has won false if he lost. */ function calculateWinnerGameType1(uint _randomNum, uint _betNum) private pure returns(bool) { assert(_betNum > 0 && _betNum < DICE_RANGE); uint resultNum = _randomNum % DICE_RANGE; // bias is negligible return resultNum < _betNum; } /** * @dev Calculate winner of game type 2 (roll higher). * @param _randomNum 256 bit random number. * @param _betNum Bet number. * @return True if user has won false if he lost. */ function calculateWinnerGameType2(uint _randomNum, uint _betNum) private pure returns(bool) { assert(_betNum >= 0 && _betNum < DICE_RANGE - 1); uint resultNum = _randomNum % DICE_RANGE; // bias is negligible return resultNum > _betNum; } } library MathUtil { /** * @dev Returns the absolute value of _val. * @param _val value * @return The absolute value of _val. */ function abs(int _val) internal pure returns(uint) { if (_val < 0) { return uint(-_val); } else { return uint(_val); } } /** * @dev Calculate maximum. */ function max(uint _val1, uint _val2) internal pure returns(uint) { return _val1 >= _val2 ? _val1 : _val2; } /** * @dev Calculate minimum. */ function min(uint _val1, uint _val2) internal pure returns(uint) { return _val1 <= _val2 ? _val1 : _val2; } } library SafeCast { /** * Cast unsigned a to signed a. */ function castToInt(uint a) internal pure returns(int) { assert(a < (1 << 255)); return int(a); } /** * Cast signed a to unsigned a. */ function castToUint(int a) internal pure returns(uint) { assert(a >= 0); return uint(a); } } library SafeMath { /** * @dev Multiplies two unsigned integers, throws on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } c = a * b; assert(c / a == b); return c; } /** * @dev Multiplies two signed integers, throws on overflow. */ function mul(int256 a, int256 b) internal pure returns (int256) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } int256 c = a * b; assert(c / a == b); return c; } /** * @dev Integer division of two unsigned integers, truncating the quotient. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return a / b; } /** * @dev Integer division of two signed integers, truncating the quotient. */ function div(int256 a, int256 b) internal pure returns (int256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 // Overflow only happens when the smallest negative int is multiplied by -1. int256 INT256_MIN = int256((uint256(1) << 255)); assert(a != INT256_MIN || b != - 1); return a / b; } /** * @dev Subtracts two unsigned integers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } /** * @dev Subtracts two signed integers, throws on overflow. */ function sub(int256 a, int256 b) internal pure returns (int256) { int256 c = a - b; assert((b >= 0 && c <= a) || (b < 0 && c > a)); return c; } /** * @dev Adds two unsigned integers, throws on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256 c) { c = a + b; assert(c >= a); return c; } /** * @dev Adds two signed integers, throws on overflow. */ function add(int256 a, int256 b) internal pure returns (int256) { int256 c = a + b; assert((b >= 0 && c >= a) || (b < 0 && c < a)); return c; } }