dice2win早期版本
原理;
https://medium.com/dapppub/fairdicedesign-315a4e253ad6
早期版本地址:
https://etherscan.io/address/0xD1CEeeef70c61da45800bd81BE3352160ad72F2a#code
https://etherscan.io/address/0xD1CEeeefA68a6aF0A5f6046132D986066c7f9426#code
pragma solidity ^0.4.23;
contract Dice2Win {
/// Constants
// Chance to win jackpot - currently 0.1%
uint256 constant JACKPOT_MODULO = 1000;
// Each bet is deducted 2% amount - 1% is house edge, 1% goes to jackpot fund.
uint256 constant HOUSE_EDGE_PERCENT = 2;
uint256 constant JACKPOT_FEE_PERCENT = 50;
// Minimum supported bet is 0.02 ETH, made possible by optimizing gas costs
// compared to our competitors.
uint256 constant MIN_BET = 0.02 ether;
// Only bets higher that 0.1 ETH have a chance to win jackpot.
uint256 constant MIN_JACKPOT_BET = 0.1 ether;
// Random number generation is provided by the hashes of future blocks.
// Two blocks is a good compromise between responsive gameplay and safety from miner attacks.
uint256 constant BLOCK_DELAY = 2;
// Bets made more than 100 blocks ago are considered failed - this has to do
// with EVM limitations on block hashes that are queryable. Settlement failure
// is most probably due to croupier bot failure, if you ever end in this situation
// ask dice2.win support for a refund!
uint256 constant BET_EXPIRATION_BLOCKS = 100;
/// Contract storage.
// Changing ownership of the contract safely
address public owner;
address public nextOwner;
// Max bet limits for coin toss/single dice and double dice respectively.
// Setting these values to zero effectively disables the respective games.
uint256 public maxBetCoinDice;
uint256 public maxBetDoubleDice;
// Current jackpot size.
uint128 public jackpotSize;
// Amount locked in ongoing bets - this is to be sure that we do not commit to bets
// that we cannot fulfill in case of win.
uint128 public lockedInBets;
/// Enum representing games
enum GameId {
CoinFlip,
SingleDice,
DoubleDice,
MaxGameId
}
uint256 constant MAX_BLOCK_NUMBER = 2 ** 56;
uint256 constant MAX_BET_MASK = 2 ** 64;
uint256 constant MAX_AMOUNT = 2 ** 128;
// Struct is tightly packed into a single 256-bit by Solidity compiler.
// This is made to reduce gas costs of placing & settlement transactions.
struct ActiveBet {
// A game that was played.
GameId gameId;
// Block number in which bet transaction was mined.
uint56 placeBlockNumber;
// A binary mask with 1 for each option.
// For example, if you play dice, the mask ranges from 000001 in binary (betting on one)
// to 111111 in binary (betting on all dice outcomes at once).
uint64 mask;
// Bet amount in wei.
uint128 amount;
}
mapping (address => ActiveBet) activeBets;
// Events that are issued to make statistic recovery easier.
event FailedPayment(address indexed _beneficiary, uint256 amount);
event Payment(address indexed _beneficiary, uint256 amount);
event JackpotPayment(address indexed _beneficiary, uint256 amount);
/// Contract governance.
constructor () public {
owner = msg.sender;
// all fields are automatically initialized to zero, which is just what's needed.
}
modifier onlyOwner {
require (msg.sender == owner);
_;
}
// This is pretty standard ownership change routine.
function approveNextOwner(address _nextOwner) public onlyOwner {
require (_nextOwner != owner);
nextOwner = _nextOwner;
}
function acceptNextOwner() public {
require (msg.sender == nextOwner);
owner = nextOwner;
}
// Contract may be destroyed only when there are no ongoing bets,
// either settled or refunded. All funds are transferred to contract owner.
function kill() public onlyOwner {
require (lockedInBets == 0);
selfdestruct(owner);
}
// Fallback function deliberately left empty. It's primary use case
// is to top up the bank roll.
function () public payable {
}
// Helper routines to alter the respective max bet limits.
function changeMaxBetCoinDice(uint256 newMaxBetCoinDice) public onlyOwner {
maxBetCoinDice = newMaxBetCoinDice;
}
function changeMaxBetDoubleDice(uint256 newMaxBetDoubleDice) public onlyOwner {
maxBetDoubleDice = newMaxBetDoubleDice;
}
// Ability to top up jackpot faster than it's natural growth by house fees.
function increaseJackpot(uint256 increaseAmount) public onlyOwner {
require (increaseAmount <= address(this).balance);
require (jackpotSize + lockedInBets + increaseAmount <= address(this).balance);
jackpotSize += uint128(increaseAmount);
}
// Funds withdrawal to cover costs of dice2.win operation.
function withdrawFunds(address beneficiary, uint256 withdrawAmount) public onlyOwner {
require (withdrawAmount <= address(this).balance);
require (jackpotSize + lockedInBets + withdrawAmount <= address(this).balance);
sendFunds(beneficiary, withdrawAmount, withdrawAmount);
}
/// Betting logic
// Bet transaction - issued by player. Contains the desired game id and betting options
// mask. Wager is the value in ether attached to the transaction.
function placeBet(GameId gameId, uint256 betMask) public payable {
// Check that there is no ongoing bet already - we support one game at a time
// from single address.
ActiveBet storage bet = activeBets[msg.sender];
require (bet.amount == 0);
// Check that the values passed fit into respective limits.
require (gameId < GameId.MaxGameId);
require (msg.value >= MIN_BET && msg.value <= getMaxBet(gameId));
require (betMask < MAX_BET_MASK);
// Determine roll parameters.
uint256 rollModulo = getRollModulo(gameId);
uint256 rollUnder = getRollUnder(rollModulo, betMask);
// Check whether contract has enough funds to process this bet.
uint256 reservedAmount = getDiceWinAmount(msg.value, rollModulo, rollUnder);
uint256 jackpotFee = getJackpotFee(msg.value);
require (jackpotSize + lockedInBets + reservedAmount + jackpotFee <= address(this).balance);
// Update reserved amounts.
lockedInBets += uint128(reservedAmount);
jackpotSize += uint128(jackpotFee);
// Store the bet parameters on blockchain.
bet.gameId = gameId;
bet.placeBlockNumber = uint56(block.number);
bet.mask = uint64(betMask);
bet.amount = uint128(msg.value);
}
// Settlement transaction - can be issued by anyone, but is designed to be handled by the
// dice2.win croupier bot. However nothing prevents you from issuing it yourself, or anyone
// issuing the settlement transaction on your behalf - that does not affect the bet outcome and
// is in fact encouraged in the case the croupier bot malfunctions.
function settleBet(address gambler) public {
// Check that there is already a bet for this gambler.
ActiveBet storage bet = activeBets[gambler];
require (bet.amount != 0);
// Check that the bet is neither too early nor too late.
require (block.number > bet.placeBlockNumber + BLOCK_DELAY);
require (block.number <= bet.placeBlockNumber + BET_EXPIRATION_BLOCKS);
// The RNG - use hash of the block that is unknown at the time of placing the bet,
// SHA3 it with gambler address. The latter step is required to make the outcomes of
// different settlement transactions mined into the same block different.
bytes32 entropy = keccak256(gambler, blockhash(bet.placeBlockNumber + BLOCK_DELAY));
uint256 diceWin = 0;
uint256 jackpotWin = 0;
// Determine roll parameters, do a roll by taking a modulo of entropy.
uint256 rollModulo = getRollModulo(bet.gameId);
uint256 dice = uint256(entropy) % rollModulo;
uint256 rollUnder = getRollUnder(rollModulo, bet.mask);
uint256 diceWinAmount = getDiceWinAmount(bet.amount, rollModulo, rollUnder);
// Check the roll result against the bet bit mask.
if ((2 ** dice) & bet.mask != 0) {
diceWin = diceWinAmount;
}
// Unlock the bet amount, regardless of the outcome.
lockedInBets -= uint128(diceWinAmount);
// Roll for a jackpot (if eligible).
if (bet.amount >= MIN_JACKPOT_BET) {
// The second modulo, statistically independent from the "main" dice roll.
// Effectively you are playing two games at once!
uint256 jackpotRng = (uint256(entropy) / rollModulo) % JACKPOT_MODULO;
// Bingo!
if (jackpotRng == 0) {
jackpotWin = jackpotSize;
jackpotSize = 0;
}
}
// Remove the processed bet from blockchain storage.
delete activeBets[gambler];
// Tally up the win.
uint256 totalWin = diceWin + jackpotWin;
if (totalWin == 0) {
totalWin = 1 wei;
}
if (jackpotWin > 0) {
emit JackpotPayment(gambler, jackpotWin);
}
// Send the funds to gambler.
sendFunds(gambler, totalWin, diceWin);
}
// Refund transaction - return the bet amount of a roll that was not processed
// in due timeframe (100 Ethereum blocks). Processing such bets is not possible,
// because EVM does not have access to the hashes further than 256 blocks ago.
//
// Like settlement, this transaction may be issued by anyone, but if you ever
// find yourself in situation like this, just contact the dice2.win support!
function refundBet(address gambler) public {
// Check that there is already a bet for this gambler.
ActiveBet storage bet = activeBets[gambler];
require (bet.amount != 0);
// The bet should be indeed late.
require (block.number > bet.placeBlockNumber + BET_EXPIRATION_BLOCKS);
// Determine roll parameters to calculate correct amount of funds locked.
uint256 rollModulo = getRollModulo(bet.gameId);
uint256 rollUnder = getRollUnder(rollModulo, bet.mask);
lockedInBets -= uint128(getDiceWinAmount(bet.amount, rollModulo, rollUnder));
// Delete the bet from the blockchain.
uint256 refundAmount = bet.amount;
delete activeBets[gambler];
// Refund the bet.
sendFunds(gambler, refundAmount, refundAmount);
}
/// Helper routines.
// Number of bet options for specific game.
function getRollModulo(GameId gameId) pure private returns (uint256) {
if (gameId == GameId.CoinFlip) {
// Heads/tails
return 2;
} else if (gameId == GameId.SingleDice) {
// One through six.
return 6;
} else if (gameId == GameId.DoubleDice) {
// 6*6=36 possible outcomes.
return 36;
}
}
// Max bet amount for a specific game.
function getMaxBet(GameId gameId) view private returns (uint256) {
if (gameId == GameId.CoinFlip) {
return maxBetCoinDice;
} else if (gameId == GameId.SingleDice) {
return maxBetCoinDice;
} else if (gameId == GameId.DoubleDice) {
return maxBetDoubleDice;
}
}
// Count 1 bits in the bet bit mask to find the total number of bet options
function getRollUnder(uint256 rollModulo, uint256 betMask) pure private returns (uint256) {
uint256 rollUnder = 0;
uint256 singleBitMask = 1;
for (uint256 shift = 0; shift < rollModulo; shift++) {
if (betMask & singleBitMask != 0) {
rollUnder++;
}
singleBitMask *= 2;
}
return rollUnder;
}
// Get the expected win amount after house edge is subtracted.
function getDiceWinAmount(uint256 amount, uint256 rollModulo, uint256 rollUnder) pure private
returns (uint256) {
require (0 < rollUnder && rollUnder <= rollModulo);
return amount * rollModulo / rollUnder * (100 - HOUSE_EDGE_PERCENT) / 100;
}
// Get the portion of bet amount that is to be accumulated in the jackpot.
function getJackpotFee(uint256 amount) pure private returns (uint256) {
return amount * HOUSE_EDGE_PERCENT / 100 * JACKPOT_FEE_PERCENT / 100;
}
// Helper routine to process the payment.
function sendFunds(address beneficiary, uint256 amount, uint256 successLogAmount) private {
if (beneficiary.send(amount)) {
emit Payment(beneficiary, successLogAmount);
} else {
emit FailedPayment(beneficiary, amount);
}
}
}
dice2win早期版本的更多相关文章
- 将“早期版本的Windows”改名
将“早期版本的Windows”改名,并修改系统等待时间 问题描述: 先装Windows XP,再装Windows 7,启动菜单会出现“早期版本的Windows”与“Windows 7”两个 ...
- 安装了SQL2005再安装SQL 2008R2,提示此计算机上安装了 Microsoft Visual Studio 2008 的早期版本和检查是否安装了 SQL Server 2005 Express 工具的解决方案
工作电脑上安装了SQL 2005, 但是客户电脑上安装的是SQL 2008R2,有时候连接他们的库调试没法连接,很不方便.然后又安装了个SQL2008 R2,期间遇到这两个问题,网上搜索了一下收到了解 ...
- SQL SERVER安装提示“安装了 Microsoft Visual Studio 2008 的早期版本
工作共遇到的问题记录: 安装Sql Server 2008 R2时提示错误:“此计算机上安装了 Microsoft Visual Studio 2008 的早期版本.请在安装 SQL Server 2 ...
- 【转】预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)
用VC++ 2008 编写C语言程序,编译出现错误: 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反) 解决方法: 建工程时 建立空项目 或者在项目设置里关闭预编 ...
- VS2005 MFC 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)
当 Visual C++ 项目启用了预编译头 (Precompiled header) 功能时,如果项目中同时混合有 .c 和 .cpp 源文件,则可能收到 C1853 编译器错误:fatal err ...
- Java 垃圾回收机制(早期版本)
Java 垃圾回收机制在我们普通理解来看,应该视为一种低优先级的后台进程来实现的,其实早期版本的Java虚拟机并非以这种方式实现的. 先从一种很简单的垃圾回收方式开始. 引用计数 引用计数是一种简单但 ...
- [转帖]Windows 10 部分早期版本已完全停止技术支持服务
Windows 10 部分早期版本已完全停止技术支持服务 2019-4-12 01:27| 发布者: cjy__05| 查看: 10186| 评论: 47|来自: pcbeta 收藏分享 转帖来源:h ...
- 从 Confluence 5.3 及其早期版本中恢复空间
如果你需要从 Confluence 5.3 及其早期版本中的导出文件恢复到晚于 Confluence 5.3 的 Confluence 中的话.你可以使用临时的 Confluence 空间安装,然后将 ...
- 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)(转)
用VC++ 2008 编写C语言程序,编译出现错误: 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反) 解决方法: 建工程时 建立空项目 或者在项目设置里关闭预编 ...
随机推荐
- FreeImage 生成带透明通道的GIF
主要方法: 加载图像及读取参数 FreeImage_Load FreeImage_GetWidth FreeImage_GetHeight FreeImage_Allocate FreeImage_G ...
- hdu_4135_Co-prime
Given a number N, you are asked to count the number of integers between A and B inclusive which are ...
- IoC和AOP扩展
一.构造注入 二.使用p命名空间注入属性值 三.注入不同数据类型 <?xml version="1.0" encoding="UTF-8"?> &l ...
- Git命令行和Xcode结合使用(我来告诉你这行代码谁写的)
现在一直使用Git来管理代码,对于有强迫症的我来说,依旧选择了命令行,下面这段话可以更好的解释我为什么喜欢使用终端敲命令. There are a lot of different ways to u ...
- tp3.2源码解析——入口文件
如果有人读这篇文章并跟着做的话,希望你能使用支持函数跳转的编辑器,还要善用var_dump和exit,对着源码去调试着看.跟着入口文件读,执行到哪里你看到哪里,对于那些不能一眼看出来的配置,则要记录下 ...
- 支付宝H5、APP支付服务端的区别(php)
php支付宝H5和APP支付1.准备工作需要前往 蚂蚁金服开放平台申请https://openhome.alipay.com/developmentDocument.htm 2.大致流程1.用户添加商 ...
- python应用:爬虫框架Scrapy系统学习第一篇——xpath详解
HTML的三大概念:标签.元素以及属性 标签:尖括号中的文本 例:<head>……</head> 标签通常成对出现 元素:标签中的所有内容 元素中可包 ...
- PAT 1001 害死人不偿命的(3n+1)猜想
1001 害死人不偿命的(3n+1)猜想 (15 分) 卡拉兹(Callatz)猜想: 对任何一个正整数 n,如果它是偶数,那么把它砍掉一半:如果它是奇数,那么把 (3n+1) 砍掉一半.这样一直反复 ...
- git小技巧之分支、关联远程仓库、回滚、解决.gitignore不生效等
1.分支管理 新建并切换分支:git checkout -b <name>新建本地分支并关联到远程分支git checkout -b myRelease origin/Release合并某 ...
- Python学习笔记八:文件操作(续),文件编码与解码,函数,递归,函数式编程介绍,高阶函数
文件操作(续) 获得文件句柄位置,f.tell(),从0开始,按字符数计数 f.read(5),读取5个字符 返回文件句柄到某位置,f.seek(0) 文件在编辑过程中改变编码,f.detech() ...