在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,它还缺乏前端交互界面,后端分配代币机制,以后补充。