jinse.com
APP
中國版App下載 Android & iPhone
金色專欄
  • 發布文章
  • 發布活動

構建數字化“分歧終端機”——探索Commit-Reveal Scheme

?1579427422427723.jpg

不知道看過電影《非誠勿擾》的人還記不記得“分歧終端機”這項奇葩發明,兩片帶把手的塑料片組成一個圓筒,有分歧的兩個人將手伸入桶中,用石頭剪刀布的方式同時出拳,然后拿開塑料片,根據猜拳結果決定聽誰的。這玩意兒乍一看很荒謬,但是不得不說確實在一定程度上保證了猜拳的絕對公平性。

對于一個善于猜拳高手來說,哪怕只有短短半秒鐘時間,也足夠他觀察出對手的手勢進而一招制敵,贏得猜拳勝利。因此,在玩猜拳游戲的時候,雙方都會盡可能的保證同時出拳。然而,在正常情況下,“絕對的同時”是不存在的,于是“分歧終端機”這個奇葩的發明似乎就有了用武之地。可如今,區塊鏈為類似的公平性問題,提供了更好的解決方案——commitment scheme.?

Commitment scheme

Commitment scheme是一種加密算法,它允許某人承諾某個值,同時將其對他人隱藏,并在以后可以對他人顯示。commitment scheme中的值具有約束力,一旦提交,任何人都無法更改它們。此方案有兩個階段:一個提交階段,該階段中需要選擇和指定一個值;另一個是揭示階段,此階段顯示和檢查該值。其實約等于一個數字化可記錄版的“分歧終端機”。

如何在以太坊上構建一個“分歧終端機”?

Commitment scheme的功能聽上去簡單,但適用范圍卻很廣。接下來就讓我們手把手的教你,如何用Commitment scheme在以太坊上創建一個數字化版本的“分歧終端機”。對于想要學習區塊鏈技術的人來說,這也不啻于一個有趣又實用的實操學習。

首先,從構造函數和初始化值開始。我們需要為“玩家”提供選擇,以猜拳游戲為例,選項為:石頭、布和剪刀。之后,要保證它們的功能只在各自的階段中運行。最后,創建一個表示玩家承諾的結構,并在構造函數中設置一些初始值、狀態變量以及事件。

具體操作如下:

contract?RockPaperScissors?{????enum?Choice?{????????None,????????Rock,????????Paper,????????Scissors????}?????enum?Stage?{????????FirstCommit,????????SecondCommit,????????FirstReveal,????????SecondReveal,????????Distribute????}?????struct?CommitChoice?{????????address?playerAddress;????????bytes32?commitment;????????Choice?choice;????}?????event?Commit(address?player);????event?Reveal(address?player,?Choice?choice);????event?Payout(address?player,?uint?amount);?????//?Initialisation?args????uint?public?bet;????uint?public?deposit;????uint?public?revealSpan;?????//?State?vars????CommitChoice[2]?public?players;????uint?public?revealDeadline;????Stage?public?stage?=?Stage.FirstCommit;?????constructor(uint?_bet,?uint?_deposit,?uint?_revealSpan)?public?{????????bet?=?_bet;????????deposit?=?_deposit;????????revealSpan?=?_revealSpan;????}}

完成上述步驟之后,接下了需要結合檢查功能來構建一個Commit Function. 該功能只被允許在兩個提交階段之一中運行。接著,我們可以確保其固有值隨交易一同發送,并且將任何額外資金退回。在完成檢查后,我們可以將承諾存儲起來,發出Commit event,并進入下一個階段。

具體操作如下:

function?commit(bytes32?commitment)?public?payable?{????//?Only?run?during?commit?stages????uint?playerIndex;????if(stage?==?Stage.FirstCommit)?playerIndex?=?0;????else?if(stage?==?Stage.SecondCommit)?playerIndex?=?1;????else?revert("both?players?have?already?played");?????uint?commitAmount?=?bet?+?deposit;????require(commitAmount?>=?bet,?"overflow?error");????require(msg.value?>=?commitAmount,?"value?must?be?greater?than?commit?amount");?????//?Return?additional?funds?transferred????if(msg.value?>?commitAmount)?{????????(bool?success,?)?=?msg.sender.call.value(msg.value?-?commitAmount)("");????????require(success,?"call?failed");????}?????//?Store?the?commitment????players[playerIndex]?=?CommitChoice(msg.sender,?commitment,?Choice.None);?????//?Emit?the?commit?event????emit?Commit(msg.sender);?????//?If?we're?on?the?first?commit,?then?move?to?the?second????if(stage?==?Stage.FirstCommit)?stage?=?Stage.SecondCommit;????//?Otherwise?we?must?already?be?on?the?second,?move?to?first?reveal????else?stage?=?Stage.FirstReveal;}

?接著,從檢查這步轉到揭示功能。此功能僅在揭示階段之一中運行,且只接受有效選擇。然后,找到玩家數據,以便于檢查他們承諾的哈希值,確定是否有效。如果該哈希值有效,對其進行存儲,觸發Reveal(揭示)事件,進入下一個階段。

?具體操作如下:

function?reveal(Choice?choice,?bytes32?blindingFactor)?public?{????//?Only?run?during?reveal?stages????require(stage?==?Stage.FirstReveal?||?stage?==?Stage.SecondReveal,?"not?at?reveal?stage");????//?Only?accept?valid?choices????require(choice?==?Choice.Rock?||?choice?==?Choice.Paper?||?choice?==?Choice.Scissors,?"invalid?choice");?????//?Find?the?player?index????uint?playerIndex;????if(players[0].playerAddress?==?msg.sender)?playerIndex?=?0;????else?if?(players[1].playerAddress?==?msg.sender)?playerIndex?=?1;????//?Revert?if?unknown?player????else?revert("unknown?player");?????//?Find?player?data????CommitChoice?storage?commitChoice?=?players[playerIndex];?????//?Check?the?hash?to?ensure?the?commitment?is?correct????require(keccak256(abi.encodePacked(msg.sender,?choice,?blindingFactor))?==?commitChoice.commitment,?"invalid?hash");?????//?Update?choice?if?correct????commitChoice.choice?=?choice;?????//?Emit?reveal?event????emit?Reveal(msg.sender,?commitChoice.choice);?????if(stage?==?Stage.FirstReveal)?{????????//?If?this?is?the?first?reveal,?set?the?deadline?for?the?second?one????????revealDeadline?=?block.number?+?revealSpan;????????require(revealDeadline?>=?block.number,?"overflow?error");????????//?Move?to?second?reveal????????stage?=?Stage.SecondReveal;????}????//?If?we're?on?second?reveal,?move?to?distribute?stage????else?stage?=?Stage.Distribute;}

?最后需要的是分配功能。我們需要在確定獲勝者之后將獎勵支付給他們。此功能也同之前每一步一樣,只能在分發階段或者揭示階段完成之后才能運行。通過對結果進行檢查,判定出獲勝的一方,之后計算獎勵,觸發Payout event,將資金發送到勝者的地址,并為下一次游戲將狀態重置。

具體操作如下:

function?distribute()?public?{????//?To?distribute?we?need:????????//?a)?To?be?in?the?distribute?stage?OR????????//?b)?Still?in?the?second?reveal?stage?but?past?the?deadline????require(stage?==?Stage.Distribute?||?(stage?==?Stage.SecondReveal?&&?revealDeadline?<=?block.number),?"cannot?yet?distribute");?????//?Calculate?value?of?payouts?for?players????uint?player0Payout;????uint?player1Payout;????uint?winningAmount?=?deposit?+?2?*?bet;????require(winningAmount?/?deposit?==?2?*?bet,?"overflow?error");?????//?If?both?players?picked?the?same?choice,?return?their?deposits?and?bets????if(players[0].choice?==?players[1].choice)?{????????player0Payout?=?deposit?+?bet;????????player1Payout?=?deposit?+?bet;????}????//?If?only?one?player?made?a?choice,?they?win????else?if(players[0].choice?==?Choice.None)?{????????player1Payout?=?winningAmount;????}????else?if(players[1].choice?==?Choice.None)?{????????player0Payout?=?winningAmount;????}????else?if(players[0].choice?==?Choice.Rock)?{????????assert(players[1].choice?==?Choice.Paper?||?players[1].choice?==?Choice.Scissors);????????if(players[1].choice?==?Choice.Paper)?{????????????//?Rock?loses?to?paper????????????player0Payout?=?deposit;????????????player1Payout?=?winningAmount;????????}????????else?if(players[1].choice?==?Choice.Scissors)?{????????????//?Rock?beats?scissors????????????player0Payout?=?winningAmount;????????????player1Payout?=?deposit;????????}?????}????else?if(players[0].choice?==?Choice.Paper)?{????????assert(players[1].choice?==?Choice.Rock?||?players[1].choice?==?Choice.Scissors);????????if(players[1].choice?==?Choice.Rock)?{????????????//?Paper?beats?rock????????????player0Payout?=?winningAmount;????????????player1Payout?=?deposit;????????}????????else?if(players[1].choice?==?Choice.Scissors)?{????????????//?Paper?loses?to?scissors????????????player0Payout?=?deposit;????????????player1Payout?=?winningAmount;????????}????}????else?if(players[0].choice?==?Choice.Scissors)?{????????assert(players[1].choice?==?Choice.Paper?||?players[1].choice?==?Choice.Rock);????????if(players[1].choice?==?Choice.Rock)?{????????????//?Scissors?lose?to?rock????????????player0Payout?=?deposit;????????????player1Payout?=?winningAmount;????????}????????else?if(players[1].choice?==?Choice.Paper)?{????????????//?Scissors?beats?paper????????????player0Payout?=?winningAmount;????????????player1Payout?=?deposit;????????}????}????else?revert("invalid?choice");?????//?Send?the?payouts????if(player0Payout?>?0)?{????????(bool?success,?)?=?players[0].playerAddress.call.value(player0Payout)("");????????require(success,?'call?failed');????????emit?Payout(players[0].playerAddress,?player0Payout);????}?else?if?(player1Payout?>?0)?{????????(bool?success,?)?=?players[1].playerAddress.call.value(player1Payout)("");????????require(success,?'call?failed');????????emit?Payout(players[1].playerAddress,?player1Payout);????}?????//?Reset?the?state?to?play?again????delete?players;????revealDeadline?=?0;????stage?=?Stage.FirstCommit;}

?接下來提供一個完整的合約內容供大家參考:

pragma solidity ^0.5.0;

?

contract RockPaperScissors {

???enum Choice {

???????None,

???????Rock,

???????Paper,

???????Scissors

??? }

?

???enum Stage {

???????FirstCommit,

???????SecondCommit,

???????FirstReveal,

???????SecondReveal,

???????Distribute

??? }

?

???struct CommitChoice {

???????address playerAddress;

???????bytes32 commitment;

???????Choice choice;

??? }

?

???event Payout(address player, uint amount);

?

??? //Initialisation args

???uint public bet;

??? uintpublic deposit;

???uint public revealSpan;

?

??? //State vars

???CommitChoice[2] public players;

???uint public revealDeadline;

???Stage public stage = Stage.FirstCommit;

?

??? constructor(uint_bet, uint _deposit, uint _revealSpan) public {

???????bet = _bet;

???????deposit = _deposit;

???????revealSpan = _revealSpan;

??? }

?

???function commit(bytes32 commitment) public payable {

???????// Only run during commit stages

??? ????uint playerIndex;

???????if(stage == Stage.FirstCommit) playerIndex = 0;

???????else if(stage == Stage.SecondCommit) playerIndex = 1;

???????else revert("both players have already played");

?

???????uint commitAmount = bet + deposit;

???????require(commitAmount >= bet, "overflow error");

???????require(msg.value >= commitAmount, "value must be greater thancommit amount");

?

???????// Return additional funds transferred

???????if(msg.value > commitAmount) {

???????????(bool success, ) = msg.sender.call.value(msg.value -commitAmount)("");

???????????require(success, "call failed");

???????}

?

???????// Store the commitment

???????players[playerIndex] = CommitChoice(msg.sender, commitment,Choice.None);

?

???????// If we're on the first commit, then move to the second

???????if(stage == Stage.FirstCommit) stage = Stage.SecondCommit;

???????// Otherwise we must already be on the second, move to first reveal

???????else stage = Stage.FirstReveal;

??? }

?

???function reveal(Choice choice, bytes32 blindingFactor) public {

???????// Only run during reveal stages

???????require(stage == Stage.FirstReveal || stage == Stage.SecondReveal,"not at reveal stage");

???????// Only accept valid choices

???????require(choice == Choice.Rock || choice == Choice.Paper || choice ==Choice.Scissors, "invalid choice");

?

???????// Find the player index

???????uint playerIndex;

???????if(players[0].playerAddress == msg.sender) playerIndex = 0;

???????else if (players[1].playerAddress == msg.sender) playerIndex = 1;

???????// Revert if unknown player

???????else revert("unknown player");

?

???????// Find player data

???????CommitChoice storage commitChoice = players[playerIndex];

?

???????// Check the hash to ensure the commitment is correct

???????require(keccak256(abi.encodePacked(msg.sender, choice, blindingFactor))== commitChoice.commitment, "invalid hash");

?

???????// Update choice if correct

???????commitChoice.choice = choice;

?

???????if(stage == Stage.FirstReveal) {

???????????// If this is the first reveal, set the deadline for the second one

???????????revealDeadline = block.number + revealSpan;

???????????require(revealDeadline >= block.number, "overflow error");

???????????// Move to second reveal

???????????stage = Stage.SecondReveal;

???????}

???????// If we're on second reveal, move to distribute stage

???????else stage = Stage.Distribute;

??? }

?

???function distribute() public {

???????// To distribute we need:

???????????// a) To be in the distribute stage OR

???????????// b) Still in the second reveal stage but past the deadline

???????require(stage == Stage.Distribute || (stage == Stage.SecondReveal&& revealDeadline <= block.number), "cannot yetdistribute");

?

???????// Calculate value of payouts for players

???????uint player0Payout;

???????uint player1Payout;

???????uint winningAmount = deposit + 2 * bet;

???????require(winningAmount / deposit == 2 * bet, "overflow error");

?

???????// If both players picked the same choice, return their deposits andbets

???????if(players[0].choice == players[1].choice) {

???????????player0Payout = deposit + bet;

???????????player1Payout = deposit + bet;

???????}

???????// If only one player made a choice, they win

???????else if(players[0].choice == Choice.None) {

???????????player1Payout = winningAmount;

???????}

???????else if(players[1].choice == Choice.None) {

???????????player0Payout = winningAmount;

???????}

???????else if(players[0].choice == Choice.Rock) {

???????????assert(players[1].choice == Choice.Paper || players[1].choice ==Choice.Scissors);

???????????if(players[1].choice == Choice.Paper) {

??????????????? // Rock loses to paper

??????????????? player0Payout = deposit;

??????????????? player1Payout = winningAmount;

???????????}

???????????else if(players[1].choice == Choice.Scissors) {

???????????????// Rock beats scissors

??????????????? player0Payout = winningAmount;

??????????????? player1Payout = deposit;

???????????}

?

???????}

???????else if(players[0].choice == Choice.Paper) {

???????????assert(players[1].choice == Choice.Rock || players[1].choice ==Choice.Scissors);

???????????if(players[1].choice == Choice.Rock) {

??????????????? // Paper beats rock

??????????????? player0Payout = winningAmount;

??????????????? player1Payout = deposit;

???????????}

???????????else if(players[1].choice == Choice.Scissors) {

??????????????? // Paper loses to scissors

??????????????? player0Payout = deposit;

??????????????? player1Payout = winningAmount;

???????????}

???????}

???????else if(players[0].choice == Choice.Scissors) {

???????????assert(players[1].choice == Choice.Paper || players[1].choice ==Choice.Rock);

???????????if(players[1].choice == Choice.Rock) {

??????????????? // Scissors lose to rock

??????????????? player0Payout = deposit;

??????????????? player1Payout = winningAmount;

???????????}

???????????else if(players[1].choice == Choice.Paper) {

??????????????? // Scissors beats paper

??????????????? player0Payout = winningAmount;

??????????????? player1Payout = deposit;

???????????}

???????}

???????else revert("invalid choice");

?

???????// Send the payouts

???????if(player0Payout > 0) {

???????????(bool success, ) =players[0].playerAddress.call.value(player0Payout)("");

???????????require(success, 'call failed');

???????????emit Payout(players[0].playerAddress, player0Payout);

???????} else if (player1Payout > 0) {

???????????(bool success, ) =players[1].playerAddress.call.value(player1Payout)("");

???????????require(success, 'call failed');

???????????emit Payout(players[1].playerAddress, player1Payout);

???????}

?

???????// Reset the state to play again

???????delete players;

???????revealDeadline = 0;

???????stage = Stage.FirstCommit;

??? }

}

以太坊是一個公共區塊鏈,可因此導致了對于隱私數據的管理困難。而有許多應用程序就像是上面提到的猜拳游戲一樣,需要隱藏值才能正常運行,因此,commitment schemes就成為了一個卓絕的解決方案。

1579427422784674.jpg

踢馬河:RaTiO Fintech合伙人,曾任某券商自營操盤手,十余年海外對沖基金和國內大型投資機構基金經理,資深交易建模專家,幣圈大咖。

請尊重原創!轉載請注明出處。

了解更多區塊鏈一線報道,與作者、讀者更深入探討、交流,歡迎添加小助手微信:jinsecaijing666, 進入[金色財經讀者交流群]。
文章作者: / 責任編輯: 我要糾錯

聲明:本文由入駐金色財經的作者撰寫,觀點僅代表作者本人,絕不代表金色財經贊同其觀點或證實其描述。

提示:投資有風險,入市須謹慎。本資訊不作為投資理財建議。

金色財經 > 區塊鏈 > 構建數字化“分歧終端機”——探索Commit-Reveal Scheme
刺激战场官网国际服 贵阳闲来麻将 波克城市版官方下载最新版本 如何网赚 西甲赛程新浪 广东闲来麻将下载 网上什么好赚钱 真人街机捕鱼大圣捕鱼 期货股票开盘 上游棋牌官网 三肖期期准 什么是私募资产配置管理人 有啥子好玩的棋牌游 皇家电玩捕鱼街机 北京麻将胡法大全 棋牌游戏信誉最好 财神捕鱼app下载