如何用区块链合约做一个赌博应用?

去中心化,匿名,可信。除了做电子货币之外,区块链仿佛是为赌博量身定制的一般。 那么问题来了,如何实现一个赌博应用呢。。

我在参考了一些代码后做了个测试,有兴趣的可以传到主链上试试(一切后果自负。这里只做技术探讨。 我参考了国外大佬写的一些代码,但出处已经找不到了。

Any possibly related consequence is  your own.

oraclize

所有的赌博都依赖一个【公开透明高可信的随机因素】。比如赌球赌马,比赛结果就放在那里,谁也不可能伪造;同时又没有人能够真正预知比赛结果(假如没有黑幕)。 你甚至可以赌下周五伦敦的气温。

那么问题来了,如何生成一个可信的随机数呢。 你说,在给定范围内random一个integer不就行了嘛? 可是程序内部生成的数字,赌徒可不买账,谁知道你是不是操纵了这个结果呢。 的确有人利用random.org之类的随机数生成服务来赌博,但是他们是不是一家谁又能证明呢。

所以就有了oraclize这个东西,它给智能合约提供了一个现实世界的api,通过它你可以在合约里动态获取货币汇率,天气情况,等等乱七八糟的数据,十分强大。但这里只说一下它的随机数服务。

oraclize的随机数生成原理可以看这里,它保证了随机数不会被操纵(除非天下矿工都是你)

使用方法概述一下是这个样子的:

0.你需要在一开始导入oraclize的库,并声明你的合约using oraclize:

1
2
import "github.com/oraclize/ethereum-api/oraclizeAPI.sol";
contract Betting is usingOraclize

btw,关于solidity版本,我在测试代码的时候4.20是稳定运行的,可最新版会出现一些不可名状的错误,我不知道为什么。

1.这个服务不是免费的,你必须支付一定的佣金。需要把这部分eth放在msg的value里面,而不是通过支付gas的方式。所以获取随机数的函数应该写成payable的

1
2
3
    function setRandomNumber()public payable{
        oraclize_query("WolframALpha","RandomInteger[{0,1}]");
    }

这里生成的是随机的0或1

2.服务提供方响应请求后,如果成功获取了随机数,就会通过回调的方式来告诉你结果,因此对应地,也需要一个处理callback的函数,用于接收,验证和处理结果。而且这一步产生的gas费用也是需要你支付的(wtf)

1
2
3
4
    function __callback(bytes32 myid, string result){
        if(msg.sender != oraclize_cbAddress()) throw;
        RandomNumber = result;
    }

这里首先要确认消息的发送方是否就是可信的的随机数提供方(防止其他人伪造结果) 代码里的RandomNumber是合约类中的成员变量。

Gambling is the demon , stay away:

基于上面的随机数,可以设计一个类似猜硬币正反的合约,两个人参与,各付一块钱押金,猜正反,赢家通吃。 下面的代码,为了方便调试,许多变量我写成public了,真正使用的话应该private。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
contract Betting is usingOraclize{
    string public description;
    address[] private addresses;
    uint public amount;           // 这里限定了下注的金额 
    uint public pot;              // 这是所有人的下注总金额
    string[] private numbers;     // 这是每个人下注的数字
    string public RandomNumber;   // 最后生成的随机数保存在这里
    mapping (address => uint) participating; // 同一个人不能投两次注
    
    event betAccepted(address gambler, uint amount);
    event Winner(address winner , uint _pot);
    event rollAccepted(address better , string _number);

这是合约的各种成员变量和事件。

接下来是构造函数:

1
2
3
4
 function Betting (string _description, uint amount_In_Ether)public payable{
        description = _description;
        amount = amount_In_Ether * 1 ether;
    }

需要注意的是,因为后面需要把奖池里的eth付给赢家,所以构造函数必须是payable的。

下面的代码用来接受投注:在调用这个方法时,应该把金额放在value里面(因此这个方法必须是payable的),前面规定投注必须是1个ETH。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  function sendNumberAndAmount (string _numberInRangeOf0To1) public payable {
        if(msg.value == amount && participating[msg.sender]==0){ 
        
        // 投注的前置条件:投注金额必须符合规定而且你之前没有下过注
            pot += msg.value;
            addresses.push(msg.sender);
            numbers.push(_numberInRangeOf0To1);
            participating[msg.sender] += 1;
            betAccepted (msg.sender,msg.value);
            rollAccepted (msg.sender, _numberInRangeOf0To1);
        }
        else {
            throw;
        }
    }

接下来把前面提到的请求随机数和回调函数抄一遍,这儿不重复了

获取随机数之后就可以检测赢家是谁并付款了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function checkingWinner()public {
    bool win = false;
    uint x = 0;
    while(win == false){
    stringsEqual(numbers[x]);
    if(equal == true){
        addresses[x].transfer(pot);
        Winner(addresses[x],pot);
        win = true ;
    }else {
        x++;
    }
}

再下面就是个坑了,虽然说我们请求的是“随机数”,但是oraclize还给我们的随机数是字符串形式,为了省一步转换,在下注的环节中也使用字符串来接受玩家的猜测数字。

那么问题来了,在solidity里面字符串怎么比较呢。。 最普遍的方法是这个样子的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
   function compareStrings(string fuck, string shit){
        bytes storage a = bytes(fuck);
        bytes memory b = bytes(shit);
        uint c = 2;

        if(a.length != b.length){
            return false;
            c--;
        }

        for(uint i = 0; i<a.length;i++){
            if(a[i]!=b[i]){
                return false;
                c--;
            }
        }
        if(c==2){
            return true;
        }
    }

肥肠无语。。不过还有种比较heck的方法可以更简单地比较字符串中的数字

1
2
3
   function compareStrings (string a, string b) view returns (bool){
       return keccak256(a) == keccak256(b);
    }

用的是hash函数来比较两个字符串,‘1’和‘0’的hash显然是不一样的,但是如果字符串没这么简单,那就不好说了。

checkingWinner()中的stringsEqual封装了一下使用compareStrings对比RandomNumber变量和输入字符串的逻辑。两行代码,这里就不写了。

反正至此你就可以跟朋友赌博去了。

THIS IS A TEST

下面是一个测试。 之前说了,oraclize是收费的,为了无限制地测试,可以使用oralize官方给出的在线ide,注意要把测试环境设置成Javascript VM. 如果她说 compiler not yet loaded , 试试关掉adBlock,并且把你的校园网关了,自己拿手机开个热点。别问我怎么知道的。

Screen Shot 2018-09-21 at 3.01.17 P

部署看上去是这样子的Screen Shot 2018-09-21 at 3.02.34 P

接下来投个注试试: 不要忘记把value写成1 ether Screen Shot 2018-09-21 at 3.03.24 P

Screen Shot 2018-09-21 at 3.04.15 P

Screen Shot 2018-09-21 at 3.08.36 P

接下来换一个账户,投“1” Screen Shot 2018-09-21 at 3.09.00 P

之后看一下合约的状态:奖池里已经有两块钱了 Screen Shot 2018-09-21 at 3.05.21 P

再调用一下设置随机数函数,这个点完了得等会儿再看结果,结果不是立刻返回给你的 注意下,这一步,你第一次用的时候是免费的,这是oraclize的政策,后面再用就得付费,费用通过value支付。具体付多少可以看官网的价格。

随机数设置好以后,就可以揭晓赢家啦。 调用checkingWinner函数: Screen Shot 2018-09-21 at 3.09.45 P 恭喜这位60c玩家获得两块钱。

improve

这个合约为了简单,省略了各种操作,还可以完善一下,比如

  • 设置一个initialize函数,把所有变量恢复初始状态,使合约可以重用
  • 可以设置更复杂的规则,更多的参加人数和可变的投注额。让投注较多的赢家赚更多
  • 你可以当庄家,不过需要根据概率设置好赔率,否则就亏了。。
  • 除了随机数,还有各种玩法,毕竟oraclize那么强大呢,在线赌球也不是不行
  • 你可以从奖池里抽取(大笔的)佣金,同时调用随机数api产生的费用也应该这么付。

只做技术探讨,一切使用后果自负

富强、民主、文明、和谐、自由、平等、公正、法治、爱国、敬业、诚信、友善



[+] click to leave a comment [+]
the comment system on this blog works via email. The button
below will generate a mailto: link based on this page's url 
and invoke your email client - please edit the comment there!

[optional] even better, encrypt the email with my public key

- don't modify the subject field
- specify a nickname, otherwise your comment will be shown as   
  anonymous
- your email address will not be disclosed
- you agree that the comment is to be made public.
- to take down a comment, send the request via email.

>> SEND COMMENT <<




Fun with Image Maps and SVGs via benji February 20, 2024

Over the past few weeks I've been playing around with making some images on my website interactive. My "informatics" class in high school taught us some basic HTML. By basic I mean in notepad and writing everything by hand, saving to a...

Goblin Week 2024 via Helvetica Blanc January 26, 2024

It snuck up on me, but I managed to draw my little goblins to celebrate the week! I love my children - they're like awful Pikmin. I've uploaded the whole parade as a print, as well as individual prints for each goblin. There's something very fu…

How to trust gpg keys via Travis Shears Personal Site October 27, 2023

After moving some GPG keys to a new computer I kept getting these trust warnings. It is NOT certain that the key belongs to the person named in the user ID.If you * really * know what you are doing, you may answer the next question with yes. Use this key a…

Generated by openring from webring