FarmEOS 是一个基于 EOS 的玩游戏挖矿 DAPP。 这个游戏还是挺火的,它们的随机数生成代码也公布在 Github 上,地址为 https://github.com/farmeos/farmeos
现在,网站上也有一些对它的分析,比如简书上的这篇文章 FarmEOS随机数研究
小弟不才,本着对 EOS 上各种合约随机数生成的爱好,稍微改了下代码,写了一个完整的范例。
假设存在一个账号 hello
,该账号下有一些 EOS 数字货币
cleos get currency balance eosio.token hello EOS 69000.0000 EOS
然后我们再看看官方开源出来的代码
uint32_t farmeos::random(account_name name, uint64_t game_id) { asset pool_eos = token(EOS_CON).get_balance(_self, EOS_SYMBOL); auto mixd = tapos_block_prefix()*tapos_block_num() + name + game_id - current_time() + pool_eos.amount; // print(" mixd", mixd); const char *mixedChar = reinterpret_cast<const char *>(&mixd); checksum256 result; sha256((char *)mixedChar, sizeof(mixedChar), &result); uint64_t random_num = *(uint64_t*)(&result.hash[0]) + *(uint64_t*)(&result.hash[8]) + *(uint64_t*)(&result.hash[16]) + *(uint64_t*)(&result.hash[24]); // print(" random:", random_num); return (uint32_t)(random_num%100 + 1); }
这段代码,最重要的其实是下面这行
asset pool_eos = token(EOS_CON).get_balance(_self, EOS_SYMBOL); auto mixd = tapos_block_prefix()*tapos_block_num() + name + game_id - current_time() + pool_eos.amount;
第一行代码用户获取某个账号下 EOS 币的余额,从某些方面说,就是当前合约下的 EOS 币的余额
asset pool_eos = token(EOS_CON).get_balance(_self, EOS_SYMBOL);
而第二行,则根据当前的块哈希,块高,游戏 id,当前时间戳和当前账号下的 EOS 余额来获取一个随机数。
auto mixd = tapos_block_prefix()*tapos_block_num() + name + game_id - current_time() + pool_eos.amount;
后面的代码就很简单了,就是根据这个随机数生成一个 checksum256 的结果,然后根据这个结果获取某些位数。
范例
上面,我们分析完了 FarmEOS 的随机数生成代码,接下来我们根据这个代码写一个范例
hello.cpp
#include <eosiolib/asset.hpp> #include <eosiolib/crypto.h> #include <eosiolib/eosio.hpp> #include <eosiolib/print.hpp> #include <eosiolib/transaction.hpp> using namespace eosio; using namespace std; class [[eosio::contract]] hello: eosio::contract { public: using eosio::contract::contract; [[eosio::action]] void num() { print("number:",random(_self,1)); } private: uint32_t random(name name, uint64_t game_id) { accounts fromAcc(eoscon,_self.value); const auto& myAcc = fromAcc.get(eossym.code().raw()); auto mixd = tapos_block_prefix()*tapos_block_num() + name.value + game_id - current_time() + myAcc.balance.amount; print(" mixd", mixd); const char *mixedChar = reinterpret_cast<const char *>(&mixd); capi_checksum256 result; sha256((char *)mixedChar, sizeof(mixedChar), &result); uint64_t random_num = *(uint64_t*)(&result.hash[0]) + *(uint64_t*)(&result.hash[8]) + *(uint64_t*)(&result.hash[16]) + *(uint64_t*)(&result.hash[24]); print(" random:", random_num); return (uint32_t)(random_num%100 + 1); } struct account { asset balance; uint64_t primary_key()const { return balance.symbol.code().raw(); } }; typedef eosio::multi_index<"accounts"_n, account> accounts; const symbol eossym = symbol("EOS", 4); const name eoscon = name("eosio.token"); }; EOSIO_DISPATCH(hello,(num))
接着使用下面的命令来编译合约
eosio-cpp -o hello.wasm hello.cpp --abigen
然后使用下面的命令来部署合约
cleos set contract hello ../hello -p hello@active
最后,我们使用下面的命令来运行 hello
合约的 num
动作
cleos push action hello num '[]' -p hello@active
多运行几次,可以发现结果如下
cleos push action hello num '[]' -p hello@active executed transaction: 69773b975c05d9bd672d8802f61ea6cead0c07f5c65ea0593077c83d1a59c0fc 96 bytes 2149 us # hello <= hello::num "" >> mixd7682472055927017715 random:1882271469984176250number:51
executed transaction: c6b1471bc3266726cef594aaba2bfad49b5dafe02e209f58f5d6953b4a7a6f66 96 bytes 291 us # hello <= hello::num "" >> mixd7682472054470129202 random:15705627791974369572number:73
目前尚无回复