前言:
以前講到Dapp原生態對隨機函數的支持並不友好, 如今講講一種解決思路. 既能保證隨機函數的不可預測性, 又能保證公平性, 平臺和玩家都能滿意. 而Dapp中的Dice2Win實現, 恰好是其中的一個經典例子.html
案例:
在講具體的思路前, 來說一下一個經典的案例:
兩人分一個蘋果, 切成兩半分, 不過兩人足夠理性且追求自身利益的最大化, 請問有什麼策略保證最大的公平性呢?
猜拳定勝負, 而後由勝利者主導分蘋果嗎? 哈哈, 這個答案顯然不合適.
提及這個例子來, 筆者印象也很深, 好想是央視有個節目李開復出的題, ^_^, 也不當心暴露了年齡.
答案是: 讓一我的來切蘋果, 而後讓另外一我的先挑.
先挑的人, 老是能獲取最好的半個蘋果, 而切蘋果爲了避免讓本身利益受損, 它會盡可能保證蘋果公平的被切開.
從這個案例中, 咱們或許能獲得一些啓示, 所謂的公平, 沒有所謂先發優點, 只有納什均衡的平衡點.app
思路:
Dice2Win的思路和上述的例子相似, 它採用混合模式, 巧妙地解決隨機數弱, 且容易被預測的問題. 其整個流程以下:
1. 玩家指定行動計劃, 並生產對應的hash值.
2. 服務端收到玩家的hash值, 產生隨機值reveal, 而後根據reveal生產commit值, 把這個返回給玩家
3. 玩家帶着commit和行動信息, 在智能合約下真正下注
4. 服務端發起結算, 帶着真正的reveal值去結算
中間的行動計劃和reveal無法中途修改, 由於有hash值的驗證
其本質的思想是hash-commit-reveal, 其核心的思想是: 服務端不知道玩家的行爲, 玩家不知道服務端真正的隨機數. 而最終結果在合約裏驗證hash, 並給出預期的結果. 這樣的流程, 保證玩家和服務端都滿意.函數
代碼:
具體的Dice2Win代碼在這, 咱們來簡單解讀一下.
placeBet代碼片斷:post
function placeBet(uint betMask, uint modulo, uint commitLastBlock, uint commit, bytes32 r, bytes32 s) external payable { Bet storage bet = bets[commit]; require (bet.gambler == address(0), "Bet should be in a 'clean' state."); // Validate input data ranges. uint amount = msg.value; require (modulo > 1 && modulo <= MAX_MODULO, "Modulo should be within range."); require (amount >= MIN_BET && amount <= MAX_AMOUNT, "Amount should be within range."); require (betMask > 0 && betMask < MAX_BET_MASK, "Mask should be within range."); // Check that commit is valid - it has not expired and its signature is valid. require (block.number <= commitLastBlock, "Commit has expired."); bytes32 signatureHash = keccak256(abi.encodePacked(uint40(commitLastBlock), commit)); require (secretSigner == ecrecover(signatureHash, 27, r, s), "ECDSA signature is not valid."); uint rollUnder; uint mask; if (modulo <= MAX_MASK_MODULO) { // Small modulo games specify bet outcomes via bit mask. // rollUnder is a number of 1 bits in this mask (population count). // This magic looking formula is an efficient way to compute population // count on EVM for numbers below 2**40. For detailed proof consult // the dice2.win whitepaper. rollUnder = ((betMask * POPCNT_MULT) & POPCNT_MASK) % POPCNT_MODULO; mask = betMask; } else { // Larger modulos specify the right edge of half-open interval of // winning bet outcomes. require (betMask > 0 && betMask <= modulo, "High modulo range, betMask larger than modulo."); rollUnder = betMask; } // Winning amount and jackpot increase. uint possibleWinAmount; uint jackpotFee; (possibleWinAmount, jackpotFee) = getDiceWinAmount(amount, modulo, rollUnder); // Enforce max profit limit. require (possibleWinAmount <= amount + maxProfit, "maxProfit limit violation."); // Lock funds. lockedInBets += uint128(possibleWinAmount); jackpotSize += uint128(jackpotFee); // Check whether contract has enough funds to process this bet. require (jackpotSize + lockedInBets <= address(this).balance, "Cannot afford to lose this bet."); // Record commit in logs. emit Commit(commit); // Store bet parameters on blockchain. bet.amount = amount; bet.modulo = uint8(modulo); bet.rollUnder = uint8(rollUnder); bet.placeBlockNumber = uint40(block.number); bet.mask = uint40(mask); bet.gambler = msg.sender; }
settleBet代碼片斷:ui
function settleBet(uint reveal, bytes32 blockHash) external onlyCroupier { uint commit = uint(keccak256(abi.encodePacked(reveal))); Bet storage bet = bets[commit]; uint placeBlockNumber = bet.placeBlockNumber; // Check that bet has not expired yet (see comment to BET_EXPIRATION_BLOCKS). require (block.number > placeBlockNumber, "settleBet in the same block as placeBet, or before."); require (block.number <= placeBlockNumber + BET_EXPIRATION_BLOCKS, "Blockhash can't be queried by EVM."); require (blockhash(placeBlockNumber) == blockHash); // Settle bet using reveal and blockHash as entropy sources. settleBetCommon(bet, reveal, blockHash); }
具體的解讀仍是留給用戶本身哈, ^_^.this
攻擊:
固然這種模式也有必定的缺點, 好比服務停止攻擊. 即玩家placeBet後, 其行動信息在鏈上可見, 這時服務能夠提早預知結果, 若輸了, 能夠停止settleBet的調用. 由於玩家不清楚對方究竟是那個隨機數, 只是看到該下注一直處於pending狀態.
因此有學者也以爲Dice2Win理論上, 也不是一個真正意義上公平遊戲, 具體參見博文: Not a fair game, Dice2win公平性分析. spa
總結:
其實我想平臺爲了長久發展, 是不太作這種傷信譽的事的. 總的來講, hash-commit-reveal這種機制, 仍是至關不錯的, 我看到很多的混合模型的dapp, 採用這種模式來保證遊戲的公平性.code