区块链研习社

在EOS上做个菠菜游戏

2019-05-12  本文已影响96人  Andytl的世界

区块链中的随机数》中介绍了一种无需各方信任产生随机数的方法,本文中我们用这个方法做一个双方对赌的菠菜游戏,玩家1给一个随机数,玩家2也给一个随机数,通过计算两个随机数得到0-99内的一个数,如果小于50则玩家1胜利,大于50则玩家2胜利。
代码原型来自:https://learneos.dev/Learn_EOS_Randomness_preview.pdf,做了些修改,让智能合约randprotocol可以跑起来。randprotocol.cpp的源代码如下:

#include <eosiolib/eosio.hpp>
#include <stdlib.h>
#include <math.h>


using namespace eosio;
using namespace std;


CONTRACT randprotocol : public eosio::contract
{
    public:
        randprotocol(name receiver, name code, datastream<const char *> ds) :
            contract(receiver, code, ds), games(receiver, receiver.value) {}
        //字符串形式的十六进制数转换为整数的方法
        uint64_t hex_to_uint64(const string &hex)
            {
                
                char *hexstr;//用指针hexstr遍历字符串中每个代表十六进制数的字符
                int length = 0;//用length记录字符串长度
                const int base = 16;     // 十六进制数的基底是16
                uint64_t decnum = 0; //记录转换的整数
                int i;//循环遍量

                // 遍历整个hex字符串,统计字符串长度
                for (hexstr = (char*)(&hex[0]); *hexstr != '\0'; hexstr++) {
                    length++;
                }
                // 计算hex字符串代表的整数值,大概思路是,用指针从高位到低位指向hex中每个字符,并将字符代表的ASCII码转换位数字,如"a"转换为10,再累加计算出字符串对应的整数值。
                hexstr = (char*)(&hex[0]);
                for (i = 0; i < length; i++, hexstr++) {
                if (*hexstr >= 48 && *hexstr <= 57) {  // *hexstr 属于 0-9
                  decnum += uint64_t((((int)(*hexstr)) - 48) * pow(base, length - i - 1));
                }
                else if ((*hexstr >= 65 && *hexstr <= 70))  {  // *hexstr 属于 A-F
                  decnum += uint64_t((((int)(*hexstr)) - 55) * pow(base, length - i - 1));
                }
                else if (*hexstr >= 97 && *hexstr <= 102) {  //  *hexstr 属于 a-f
                  decnum += uint64_t((((int)(*hexstr)) - 87) * pow(base, length - i - 1));
                }
                else {
                    break;
                }
                }
                return decnum;
            }
               //定义智能合约中的结构体,用于记录数据
        struct [[eosio::table]] game_s
        {
            name player1;
            name player2;
            checksum256 p1_commitment;
            uint64_t p2_rand;

            auto primary_key() const { return player1.value; }
            EOSLIB_SERIALIZE(game_s, (player1)(player2)(p1_commitment)(p2_rand))
        };

        typedef eosio::multi_index<"games"_n, game_s> games_t;
        games_t games;
               //player1首先发送给智能合约一个随机数的哈希值,也就是创建一个“承诺交易”
        ACTION create(name player1, const eosio::checksum256 &commitment)
        {
            require_auth(player1);
            auto itr = games.find(player1.value);
            eosio::check(itr == games.end(), "finish existing game first");

            games.emplace(player1, [&](auto &g) {
                g.player1 = player1;
                g.p1_commitment = commitment;
            });

        }
              //player2响应player1的交易,并上传自己的随机数
        ACTION respond(name player2, name game_host, const string &randomness_as_hex)

        {
            require_auth(player2);
            auto itr = games.find(game_host.value);
            eosio::check(itr != games.end(), "game does not exist");
            uint64_t randomness = hex_to_uint64(randomness_as_hex);

            games.modify(itr, ""_n, [&](auto &g) {
                g.player2 = player2;
                g.p2_rand = randomness;
        });

        }
               //player1揭露自己的随机数,并验证是否位承诺交易的随机数。得到两个人的随机数后,智能合约计算出一个在[0,100)范围内的随机数,如果小于50则player1胜利,大于50则player2胜利
        ACTION reveal(name player1, const string &p1_rand_as_hex)
        {
            require_auth(player1);
            auto itr = games.find(player1.value);
            eosio::check(itr != games.end(), "game does not exist");
            eosio::check(itr->player2.value > 0, "no other player responded yet");

            // checks if sha256(p1_rand_as_hex, bytes_size) == third argument
            assert_sha256(
                (char *)p1_rand_as_hex.c_str(),
                p1_rand_as_hex.size(),
                itr->p1_commitment);
            uint64_t randomness = hex_to_uint64(p1_rand_as_hex) ^ itr->p2_rand;
            randomness = randomness % 100;
            eosio::print(("Rolled " + to_string(randomness) + ". ").c_str());
            if (randomness < 50)
            {
                eosio::print("Player 1 wins!");
            }
            else
            {
                eosio::print("Player 2 wins!");
            }

            games.erase(itr);

        }

};

发布智能合约randprotocol的过程与《手把手教你发行EOS代币》很类似。也可以把上面代码拷贝到在线eos智能合约开发平台Beosin中,几分钟就能编译、部署、执行智能合约,非常方便。只不过Beosin中的debug功能还没完善,在线调试代码很麻烦。

在本地环境中可以验证合约执行情况,如下:

# 计算一个随机数
openssl rand -hex 8
# outputs 021cb69650e3dd84

# 计算随机数的哈希值
echo -n "021cb69650e3dd84" | openssl dgst -sha256
# outputs eb162b76b7830c61e22c3379f83bafc212d408698c9bbeb3923ffb594a3918bd

# player1发起承诺交易
cleos push action rand create '{"player1": "test1", "commitment": "eb162b76b7830c..."}' -p test1

# player2 产生随机数并发起响应
openssl rand -hex 8
# outputs 981b3e64607e49c4
cleos push action rand respond '{"player2": "test2", "game_host": "test1",
"randomness_as_hex": "981b3e64607e49c4"}' -p test2

# player1 揭露随机数并计算出结果
cleos push action rand reveal '{"player1": "test1", "p1_rand_as_hex": "021cb69650e3dd84"}' -p test1

# >> Rolled 76. Player 2 wins!

也可以参考上述代码参数,在Beosin环境中执行智能合约,以验证智能合约。一个简单的菠菜游戏就诞生了,作为一个dapp,它还缺乏前端交互界面,后端分配代币机制,以后补充。

上一篇下一篇

猜你喜欢

热点阅读