先基本记录下,稍后整理,测试,总结
1. How can I generate random numbers inside a smart contract?
if( !is_zero(prev_reveal.reveal) ) {
checksum256 result;
sha256( (char *)&game_itr->player1, sizeof(player)*2, &result);
auto prev_revealer_offer = idx.find( offer::get_commitment(prev_reveal.commitment) );
int winner = result.hash[1] < result.hash[0] ? 0 : 1;
if( winner ) {
pay_and_clean(*game_itr, *curr_revealer_offer, *prev_revealer_offer);
} else {
pay_and_clean(*game_itr, *prev_revealer_offer, *curr_revealer_offer);
}
}
2. EOSDACRandom
#include "eosdacrandom.hpp"
#include <eosiolib/system.h>
#include <eosiolib/stdlib.hpp>
#include <eosiolib/action.hpp>
#include <eosiolib/symbol.hpp>
#include "../eosdactoken/eosdactoken.hpp"
using namespace eosio;
eosdacrandom::eosdacrandom(account_name name)
: contract(name),
_seeds(_self, name),
_geters(_self, name)
{
}
eosdacrandom::~eosdacrandom()
{
}
void eosdacrandom::setsize(uint64_t size)
{
require_auth(_self);
seedconfig_table config(_self, _self);
auto existing = config.find(_self);
if (existing != config.end()) {
if (existing->hash_count == existing->target_size) {
if (existing->hash_count != existing->seed_match) {
eosio_assert(false, "do not set size during sendseed period");
}
}
config.modify(existing, _self, [&](auto& c){
c.target_size = size;
});
} else {
config.emplace(_self, [&](auto& c){
c.owner = _self;
c.target_size = size;
});
}
}
void eosdacrandom::sendseed(name owner, int64_t seed, string sym)
{
eosio_assert(is_account(owner), "Invalid account");
symbol_type symbol(string_to_symbol(4, sym.c_str()));
eosio::asset fromBalance = eosdactoken(N(eosdactoken)).get_balance(owner, symbol.name());
eosio_assert(fromBalance.amount > 0, "account has not enough OCT to do it");
eosio::asset selfBalance = eosdactoken(N(eosdactoken)).get_balance(_self, symbol.name());
eosio_assert(selfBalance.amount > 0, "contract account has not enough OCT to do it");
require_auth(owner);
seedconfig_table config(_self, _self);
auto existing = config.find(_self);
eosio_assert(existing != config.end(), "target size must set first");
const auto& cfg = *existing;
const auto& sd = _seeds.find(owner);
if (cfg.hash_count < cfg.target_size) {
if (sd != _seeds.end()) {
_seeds.erase(sd);
config.modify(cfg, _self, [&](auto& c){
c.hash_count --;
});
}
eosio_assert(false, "hash size not fulled");
}
string h = cal_sha256_str(seed);
eosio_assert(sd->seed != seed, "you have already send seed");
if (sd->hash != h) {
//SEND_INLINE_ACTION( eosdacvote, vote, {_self,N(active)}, {_self, owner, selfBalance, false} );
for (auto itr = _seeds.cbegin(); itr != _seeds.cend(); ) {
itr = _seeds.erase(itr);
}
config.modify(cfg, _self, [&](auto& c){
c.hash_count = 0;
});
return;
}
_seeds.modify(sd, _self, [&](auto& a){
a.seed = seed;
});
config.modify(cfg, _self, [&](auto& c){
c.seed_match = c.seed_match + 1;
print(c.seed_match);
});
// if all seeds match
if (seedsmatch()) {
dispatch_request(owner);
}
}
void eosdacrandom::sendhash(name owner, string hash, string sym)
{
eosio_assert(is_account(owner), "Invalid account");
seedconfig_table config(_self, _self);
auto existing = config.find(_self);
eosio_assert(existing != config.end(), "target size must set first");
const auto& cfg = *existing;
eosio_assert(cfg.hash_count < cfg.target_size, "seeds is full");
symbol_type symbol(string_to_symbol(4, sym.c_str()));
eosio::asset fromBalance = eosdactoken(N(eosdactoken)).get_balance(owner, symbol.name());
eosio_assert(fromBalance.amount > 0, "account has not enough OCT to do it");
require_auth(owner);
auto s = _seeds.find(owner);
if (s == _seeds.end()) {
_seeds.emplace(_self, [&](auto& a){
a.owner = owner;
a.hash = hash;
});
config.modify(cfg, _self, [&](auto& c){
c.hash_count++;
});
} else {
_seeds.modify(s, _self, [&](auto& a){
a.hash = hash;
});
}
}
void eosdacrandom::regrequest(name owner, uint64_t index)
{
eosio_assert(is_account(owner), "Invalid account");
uint64_t cur = current_time();
auto it = _geters.find(owner);
request_info req {index, cur};
if (it == _geters.end()) {
_geters.emplace(_self, [&](auto& a){
a.owner = owner;
a.requestinfo.push_back(req);
});
} else {
_geters.modify(it, _self, [&](auto& a){
bool same_idx = false;
for (auto ri = a.requestinfo.begin(); ri != a.requestinfo.end(); ++ri) {
if (ri->index == index) { // if index equals former index.
*ri = req;
same_idx = true;
break;
}
}
if (!same_idx) {
a.requestinfo.push_back(req);
}
});
}
}
int64_t eosdacrandom::random()
{
// use _seeds to generate random number
seedconfig_table config(_self, _self);
auto existing = config.find(_self);
eosio_assert(existing != config.end(), "target size must set first");
eosio_assert(existing->hash_count == existing->target_size, "seed is not full");
// how to generate random number?
int64_t seed = 0;
for (auto it = _seeds.cbegin(); it != _seeds.cend();) {
seed += it->seed;
it = _seeds.erase(it);
}
config.modify(existing, _self, [&](auto& c){
c.hash_count = 0;
c.seed_match = 0;
});
checksum256 cs = cal_sha256(seed);
return (((int64_t)cs.hash[0] << 56 ) & 0xFF00000000000000U)
| (((int64_t)cs.hash[1] << 48 ) & 0x00FF000000000000U)
| (((int64_t)cs.hash[2] << 40 ) & 0x0000FF0000000000U)
| (((int64_t)cs.hash[3] << 32 ) & 0x000000FF00000000U)
| ((cs.hash[4] << 24 ) & 0x00000000FF000000U)
| ((cs.hash[5] << 16 ) & 0x0000000000FF0000U)
| ((cs.hash[6] << 8 ) & 0x000000000000FF00U)
| (cs.hash[7] & 0x00000000000000FFU);
}
bool eosdacrandom::seedsmatch()
{
seedconfig_table config(_self, _self);
auto existing = config.find(_self);
eosio_assert(existing != config.end(), "target size must set first");
const auto& cfg = *existing;
if (cfg.hash_count != cfg.target_size) {
return false;
}
if (cfg.hash_count == cfg.seed_match) {
return true;
}
return false;
}
checksum256 eosdacrandom::cal_sha256(int64_t word)
{
checksum256 cs = { 0 };
char d[255] = { 0 };
snprintf(d, sizeof(d) - 1, "%lld", word);
sha256(d, strlen(d), &cs);
return cs;
}
string eosdacrandom::cal_sha256_str(int64_t word)
{
string h;
checksum256 cs = cal_sha256(word);
for (int i = 0; i < sizeof(cs.hash); ++i) {
char hex[3] = { 0 };
snprintf(hex, sizeof(hex), "%02x", static_cast<unsigned char>(cs.hash[i]));
h += hex;
}
return h;
}
void eosdacrandom::dispatch_request(name owner)
{
print("all seeds matched.");
uint64_t cur = current_time();
int64_t num = random();
static int expiraion = 3000; // ms
std::vector<std::vector<request_info>> reqs;
for (auto i = _geters.cbegin(); i != _geters.cend();) {
std:vector<request_info> tmp(i->requestinfo);
for (auto j = tmp.begin(); j != tmp.end();) {
if (cur - j->timestamp >= expiraion) {
dispatch_inline(i->owner, string_to_name("getrandom"),
{permission_level(_self, N(active))},
std::make_tuple(j->index, num));
j = tmp.erase(j);
} else {
++j;
}
}
if (tmp.size()) {
reqs.push_back(tmp);
}
i = _geters.erase(i);
}
if (reqs.size()) {
for (auto r = reqs.cbegin(); r != reqs.cend(); ++r) {
_geters.emplace(_self, [&](auto& a){
a.owner = owner;
a.requestinfo = *r;
});
}
}
}
EOSIO_ABI( eosdacrandom, (setsize) (sendseed) (sendhash) (regrequest) )
3. eoscraper
#include "scraper.hpp"
#include <set>
#include <map>
#include <vector>
#include "Proof.hh"
#include "Ecc.hh"
//count of participants for random generation (min 4)
constexpr static uint8_t participant_cnt = 10;
static const UInt256 seed_for_g = fromString("21343453542133456453212");
static const UInt256 seed_for_m = fromString("56987654354236234435425");
static const Point base_point { Secp256k1, Secp256k1.G.x, Secp256k1.G.y };
static const Point gen_g = base_point.scalarMult(seed_for_g);
static const Point gen_m = base_point.scalarMult(seed_for_m);
struct binuint256_t {
checksum256 data;
};
struct binpoint {
binuint256_t x, y;
};
struct binproof {
binpoint g,m,h,z,a,b;
binuint256_t c, r;
};
UInt256 fromBin(binuint256_t const & data) {
UInt256 x;
memcpy(x.data, data.data.hash, sizeof(data.data));
return std::move(x);
}
Point fromBin(binpoint const &data) {
Point point {Secp256k1, fromBin(data.x), fromBin(data.y)};
eosio_assert(point.isOnCurve(), "Point not in curve");
return std::move(point);
}
Proof fromBin(binproof const &data) {
return {
fromBin(data.g), fromBin(data.m),
fromBin(data.h), fromBin(data.z),
fromBin(data.a), fromBin(data.b),
fromBin(data.c), fromBin(data.r)
};
}
// @abi table
struct encshare {
uint64_t id;
account_name from;
account_name to;
binpoint data;
binpoint commitment;
binproof proof;
uint64_t dec_id;
auto primary_key() const { return id; }
EOSLIB_SERIALIZE(encshare, (id)(from)(to)(data)(commitment)(proof)(dec_id));
};
// @abi table
struct decshare {
uint64_t id;
binpoint s;
binproof proof;
auto primary_key() const { return id; }
EOSLIB_SERIALIZE(decshare, (id)(s)(proof));
};
// @abi table
struct random {
enum state : uint8_t {
wait_joins = 0,
wait_enc_shares,
wait_dec_shares,
done,
error,
};
uint64_t id;
uint8_t state;
uint8_t joined_cnt;
std::vector<account_name> participants;
uint32_t pushed_cnt;
std::vector<uint64_t> enc_ids;
std::vector<uint8_t> dec_cnts;
auto primary_key()const { return id; }
EOSLIB_SERIALIZE(random, (id)(state)(participants)(enc_ids)(dec_cnts));
};
// @abi table
struct account {
account_name owner;
binpoint pubkey;
auto primary_key()const { return owner; }
EOSLIB_SERIALIZE(account, (owner)(pubkey));
};
class scraper : public eosio::contract {
public:
scraper( account_name self ) :
contract(self),
_accounts(_self, _self),
_randoms( _self, _self),
_encshares(_self, _self),
_decshares(_self, _self)
{ }
// @abi action
void bindkey(account_name sender, binpoint pubkey) {
require_auth(sender);
auto acc = _accounts.find(sender);
if (acc != _accounts.end())
_accounts.modify(acc, sender, [&] (auto & acc) {
acc.pubkey = pubkey;
});
else
_accounts.emplace(sender, [&](auto & acc) {
acc.owner = sender;
acc.pubkey = pubkey;
});
}
// @abi action
void initrand(account_name sender) {
require_auth(sender);
auto acc = _accounts.find(sender);
eosio_assert(acc != _accounts.end(), "Need to bind public key");
_randoms.emplace(sender, [&](auto & rand) {
rand.id = _randoms.available_primary_key();
rand.state = random::state::wait_joins;
rand.participants.reserve(participant_cnt);
rand.enc_ids.reserve(participant_cnt*participant_cnt);
rand.dec_cnts.reserve(participant_cnt);
rand.participants[0] = sender;
rand.joined_cnt = 1;
rand.pushed_cnt = 0;
for(auto k = 0; k < participant_cnt * participant_cnt; ++k)
rand.enc_ids[k] = -1;
});
}
// @abi action
void joinrand(uint64_t rand_id, account_name sender) {
require_auth(sender);
auto acc = _accounts.find(sender);
eosio_assert(acc != _accounts.end(), "Need to bind public key");
auto rand_it = _randoms.find(rand_id);
eosio_assert(rand_it != _randoms.end(), "Rand not found");
eosio_assert(participantId(*rand_it, sender) != -1, "Already joinded");
eosio_assert(rand_it->state == random::state::wait_joins, "Invalid state");
_randoms.modify(rand_it, sender, [&](auto & rand) {
rand.participants[rand.joined_cnt++] = sender;
if (rand.joined_cnt == participant_cnt)
rand.state = random::state::wait_enc_shares;
});
}
// @abi action
void pushencshare(uint64_t rand_id, account_name sender, account_name receiver,
binpoint data, binpoint commitment, binproof proof)
{
require_auth(sender);
auto senderAccIt = _accounts.find(sender);
eosio_assert(senderAccIt != _accounts.end(), "Sender not found");
auto receiverAccIt = _accounts.find(receiver);
eosio_assert(receiverAccIt != _accounts.end(), "Receiver not found");
auto rand_it = _randoms.find(rand_id);
eosio_assert(rand_it != _randoms.end(), "Rand not found");
auto senderPartId = participantId(*rand_it, sender);
eosio_assert(senderPartId != -1, "Sender is not participant");
auto receiverPartId = participantId(*rand_it, receiver);
eosio_assert(receiverPartId != -1, "Receiver is not participant");
eosio_assert(rand_it->enc_ids[(uint32_t)senderPartId * participant_cnt + receiverPartId] == -1, "share already pushed");
auto pk = fromBin(receiverAccIt->pubkey);
auto pr = fromBin(proof);
eosio_assert(gen_g == pr.g, "Invalid proof (gen_g != g)");
eosio_assert(pk == pr.m, "Invalid proof (pk != m)");
eosio_assert(pr.verify(), "Proof validate failed");
uint64_t new_share_id;
_encshares.emplace(sender, [&](auto & share) {
new_share_id = _encshares.available_primary_key();
share.id = new_share_id;
share.from = sender;
share.to = receiver;
share.data = data;
share.commitment = commitment;
share.proof = proof;
share.dec_id = (uint64_t)-1;
});
_randoms.modify(rand_it, sender, [&](auto & rand) {
rand.enc_ids[(uint32_t)senderPartId * participant_cnt + receiverPartId] = new_share_id;
rand.pushed_cnt++;
if (rand.pushed_cnt == (uint32_t)participant_cnt * participant_cnt)
rand.state = random::state::wait_dec_shares;
});
}
// @abi action
void pushdecshare(uint64_t rand_id, account_name sender, account_name from,
binpoint s, binproof proof)
{
require_auth(sender);
auto senderAccIt = _accounts.find(sender);
eosio_assert(senderAccIt != _accounts.end(), "Sender not found");
auto fromAccIt = _accounts.find(from);
eosio_assert(fromAccIt != _accounts.end(), "From not found");
auto rand_it = _randoms.find(rand_id);
eosio_assert(rand_it != _randoms.end(), "Rand not found");
eosio_assert(rand_it->state == random::state::wait_dec_shares, "Invalid state");
auto senderPartId = participantId(*rand_it, from);
eosio_assert(senderPartId != -1, "Sender is not participant");
auto receiverPartId = participantId(*rand_it, sender);
eosio_assert(receiverPartId != -1, "Receiver is not participant");
auto enc_id = rand_it->enc_ids[(uint32_t)senderPartId * participant_cnt + receiverPartId];
auto encshare_it = _encshares.find(enc_id);
eosio_assert(encshare_it != _encshares.end(), "Share not found");
eosio_assert(encshare_it->dec_id == -1, "Already pushed");
auto encdata = fromBin(encshare_it->data);
auto pk = fromBin(senderAccIt->pubkey);
auto ss = fromBin(s);
auto pr = fromBin(proof);
eosio_assert(gen_g == pr.g, "Invalid proof (gen_g != g)");
eosio_assert(encdata == pr.z, "Invalid proof (encdata != g)");
eosio_assert(ss == pr.m, "Invalid proof (m != s)");
eosio_assert(pk == pr.h, "Invalid proof (pk != h)");
eosio_assert(pr.verify(), "Proof validate failed");
uint64_t new_dec_id;
_decshares.emplace(sender, [&](auto & obj) {
new_dec_id = _decshares.available_primary_key();
obj.id = new_dec_id;
obj.s = s;
obj.proof = proof;
});
_encshares.modify(encshare_it, sender, [&](auto & obj) {
obj.dec_id = new_dec_id;
});
_randoms.modify(rand_it, sender, [&](auto & rand) {
rand.dec_cnts[from]++;
});
}
private:
eosio::multi_index<N(accounts), account> _accounts;
eosio::multi_index<N(randoms), random> _randoms;
eosio::multi_index<N(encshare), encshare> _encshares;
eosio::multi_index<N(decshare), decshare> _decshares;
private:
uint8_t participantId(random const & obj, account_name acc) {
for (uint8_t i = 0; i < participant_cnt; ++i)
if (obj.participants[i] == acc)
return i;
return -1;
}
};
EOSIO_ABI( scraper, (bindkey)(initrand)(joinrand)(pushencshare)(pushdecshare))