Dapp混合模型開發--Dice2win的解讀

 

前言:
  以前講到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

相關文章
相關標籤/搜索