未验证 提交 941558f4 编写于 作者: K Kevin Heifner 提交者: GitHub

Merge pull request #2448 from EOSIO/fix-dice-contract

Add example dice game session using cleos
......@@ -43,4 +43,227 @@ Potential Vulnerabilities
- under normal operation of DPOS chains there are few if any chain reorganizations
Example game session using cleos
-------
#### Prerequisites
* Wallet must be unlock and have at least the following private keys
**5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3**
**5Jmsawgsp1tQ3GD6JyGCwy1dcvqKZgX6ugMVMdjirx85iv5VyPR**
##### Upload bios contract
````bash
cleos set contract eosio build/contracts/eosio.bios -p eosio
````
##### Ceate eosio.token account
````bash
cleos create account eosio eosio.token EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4
````
##### Set eosio.token contract to eosio.token account
````bash
cleos set contract eosio.token build/contracts/eosio.token -p eosio.token
````
##### Create dice account
````bash
cleos create account eosio dice EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4
````
##### Set dice contract to dice account
````bash
cleos set contract dice build/contracts/dice -p dice
````
##### Create native EOS token
````bash
cleos push action eosio.token create '[ "eosio", "1000000000.0000 EOS", 0, 0, 0]' -p eosio.token
````
##### Create alice account
````bash
cleos create account eosio alice EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4
````
##### Create bob account
````bash
cleos create account eosio bob EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4
````
##### Issue 1000 EOS to alice
````bash
cleos push action eosio.token issue '[ "alice", "1000.0000 EOS", "" ]' -p eosio
````
##### Issue 1000 EOS to bob
````bash
cleos push action eosio.token issue '[ "bob", "1000.0000 EOS", "" ]' -p eosio
````
##### Allow dice contract to make transfers on alice behalf (deposit)
````bash
cleos set account permission alice active '{"threshold": 1,"keys": [{"key": "EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4","weight": 1}],"accounts": [{"permission":{"actor":"dice","permission":"active"},"weight":1}]}' owner -p alice
````
##### Allow dice contract to make transfers on bob behalf (deposit)
````bash
cleos set account permission bob active '{"threshold": 1,"keys": [{"key": "EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4","weight": 1}],"accounts": [{"permission":{"actor":"dice","permission":"active"},"weight":1}]}' owner -p bob
````
##### Alice deposits 100 EOS into the dice contract
````bash
cleos push action dice deposit '[ "alice", "100.0000 EOS" ]' -p alice
````
##### Bob deposits 100 EOS into the dice contract
````bash
cleos push action dice deposit '[ "bob", "100.0000 EOS" ]' -p bob
````
##### Alice generates a secret
````bash
openssl rand 32 -hex
28349b1d4bcdc9905e4ef9719019e55743c84efa0c5e9a0b077f0b54fcd84905
````
##### Alice generates sha256(secret)
````bash
echo -n '28349b1d4bcdc9905e4ef9719019e55743c84efa0c5e9a0b077f0b54fcd84905' | xxd -r -p | sha256sum -b | awk '{print $1}'
d533f24d6f28ddcef3f066474f7b8355383e485681ba8e793e037f5cf36e4883
````
##### Alice bets 3 EOS
````bash
cleos push action dice offerbet '[ "3.0000 EOS", "alice", "d533f24d6f28ddcef3f066474f7b8355383e485681ba8e793e037f5cf36e4883" ]' -p alice
````
##### Bob generates a secret
````bash
openssl rand 32 -hex
15fe76d25e124b08feb835f12e00a879bd15666a33786e64b655891fba7d6c12
````
##### Bob generates sha256(secret)
````bash
echo -n '15fe76d25e124b08feb835f12e00a879bd15666a33786e64b655891fba7d6c12' | xxd -r -p | sha256sum -b | awk '{print $1}'
50ed53fcdaf27f88d51ea4e835b1055efe779bb87e6cfdff47d28c88ffb27129
````
##### Bob also bets 3 EOS (a game is started)
````bash
cleos push action dice offerbet '[ "3.0000 EOS", "bob", "50ed53fcdaf27f88d51ea4e835b1055efe779bb87e6cfdff47d28c88ffb27129" ]' -p bob
````
##### Dice contract tables right after the game started
````bash
cleos get table dice dice account
````
````json
{
"rows": [{
"owner": "alice",
"eos_balance": "97.0000 EOS",
"open_offers": 0,
"open_games": 1
},{
"owner": "bob",
"eos_balance": "97.0000 EOS",
"open_offers": 0,
"open_games": 1
}
],
"more": false
}
````
````bash
cleos get table dice dice game
````
````json
{
"rows": [{
"id": 1,
"bet": "3.0000 EOS",
"deadline": "1970-01-01T00:00:00",
"player1": {
"commitment": "d533f24d6f28ddcef3f066474f7b8355383e485681ba8e793e037f5cf36e4883",
"reveal": "0000000000000000000000000000000000000000000000000000000000000000"
},
"player2": {
"commitment": "50ed53fcdaf27f88d51ea4e835b1055efe779bb87e6cfdff47d28c88ffb27129",
"reveal": "0000000000000000000000000000000000000000000000000000000000000000"
}
}
],
"more": false
}
````
##### Bob reveals his secret
````bash
cleos push action dice reveal '[ "50ed53fcdaf27f88d51ea4e835b1055efe779bb87e6cfdff47d28c88ffb27129", "15fe76d25e124b08feb835f12e00a879bd15666a33786e64b655891fba7d6c12" ]' -p bob
````
##### Game table after bob revealed (now the game has a deadline for alice to reveal)
````bash
cleos get table dice dice game
````
````json
{
"rows": [{
"id": 1,
"bet": "3.0000 EOS",
"deadline": "2018-04-17T07:45:49",
"player1": {
"commitment": "d533f24d6f28ddcef3f066474f7b8355383e485681ba8e793e037f5cf36e4883",
"reveal": "0000000000000000000000000000000000000000000000000000000000000000"
},
"player2": {
"commitment": "50ed53fcdaf27f88d51ea4e835b1055efe779bb87e6cfdff47d28c88ffb27129",
"reveal": "15fe76d25e124b08feb835f12e00a879bd15666a33786e64b655891fba7d6c12"
}
}
],
"more": false
}
````
##### Alice reveals her secret (the winner is determined, the game is removed)
````bash
cleos push action dice reveal '[ "d533f24d6f28ddcef3f066474f7b8355383e485681ba8e793e037f5cf36e4883", "28349b1d4bcdc9905e4ef9719019e55743c84efa0c5e9a0b077f0b54fcd84905" ]' -p alice
````
##### Balance of the accounts after game ends
````bash
cleos get table dice dice account
````
````json
{
"rows": [{
"owner": "alice",
"eos_balance": "103.0000 EOS",
"open_offers": 0,
"open_games": 0
},{
"owner": "bob",
"eos_balance": "97.0000 EOS",
"open_offers": 0,
"open_games": 0
}
],
"more": false
}
````
##### Alice withdraw from her dice account 103 EOS
````bash
cleos push action dice withdraw '[ "alice", "103.0000 EOS" ]' -p alice
````
##### Balance of alice after withdraw
````bash
cleos get currency balance eosio.token alice eos
1003.0000 EOS
````
......@@ -5,22 +5,20 @@
#include <utility>
#include <vector>
#include <eosiolib/crypto.h>
#include <eosiolib/types.hpp>
#include <eosiolib/token.hpp>
#include <eosiolib/print.hpp>
#include <eosiolib/action.hpp>
#include <eosiolib/multi_index.hpp>
#include <string>
#include <eosiolib/eosio.hpp>
#include <eosiolib/asset.hpp>
#include <eosiolib/contract.hpp>
#include <eosio.system/eosio.system.hpp>
using eos_currency = eosiosystem::contract<N(eosio)>::currency;
#include <eosiolib/crypto.h>
using eosio::key256;
using eosio::indexed_by;
using eosio::const_mem_fun;
using eosio::asset;
using eosio::permission_level;
using eosio::action;
using eosio::print;
using eosio::name;
class dice : public eosio::contract {
public:
......@@ -37,9 +35,10 @@ class dice : public eosio::contract {
//@abi action
void offerbet(const asset& bet, const account_name player, const checksum256& commitment) {
auto amount = eos_currency::token_type(bet);
eosio_assert( bet.symbol == S(4,EOS) , "only EOS token allowed" );
eosio_assert( bet.is_valid(), "invalid bet" );
eosio_assert( bet.amount > 0, "must bet positive quantity" );
eosio_assert( amount.quantity > 0, "invalid bet" );
eosio_assert( !has_offer( commitment ), "offer with this commitment already exist" );
require_auth( player );
......@@ -60,12 +59,13 @@ class dice : public eosio::contract {
auto matched_offer_itr = idx.lower_bound( (uint64_t)new_offer_itr->bet.amount );
if( matched_offer_itr == idx.end()
|| matched_offer_itr->bet.amount != new_offer_itr->bet.amount
|| matched_offer_itr->bet != new_offer_itr->bet
|| matched_offer_itr->owner == new_offer_itr->owner ) {
// No matching bet found, update player's account
accounts.modify( cur_player_itr, 0, [&](auto& acnt) {
acnt.eos_balance = (asset)(eos_currency::token_type(acnt.eos_balance) - amount);
eosio_assert( acnt.eos_balance >= bet, "insufficient balance" );
acnt.eos_balance -= bet;
acnt.open_offers++;
});
......@@ -114,7 +114,8 @@ class dice : public eosio::contract {
});
accounts.modify( cur_player_itr, 0, [&](auto& acnt) {
acnt.eos_balance = (asset)(eos_currency::token_type(acnt.eos_balance) - amount);
eosio_assert( acnt.eos_balance >= bet, "insufficient balance" );
acnt.eos_balance -= bet;
acnt.open_games++;
});
}
......@@ -133,7 +134,7 @@ class dice : public eosio::contract {
auto acnt_itr = accounts.find(offer_itr->owner);
accounts.modify(acnt_itr, 0, [&](auto& acnt){
acnt.open_offers--;
acnt.eos_balance.amount += offer_itr->bet.amount;
acnt.eos_balance += offer_itr->bet;
});
idx.erase(offer_itr);
......@@ -212,7 +213,10 @@ class dice : public eosio::contract {
}
//@abi action
void deposit( const account_name from, const asset& a ) {
void deposit( const account_name from, const asset& quantity ) {
eosio_assert( quantity.is_valid(), "invalid quantity" );
eosio_assert( quantity.amount > 0, "must deposit positive quantity" );
auto itr = accounts.find(from);
if( itr == accounts.end() ) {
......@@ -221,27 +225,37 @@ class dice : public eosio::contract {
});
}
auto amount = eos_currency::token_type(a);
action(
permission_level{ from, N(active) },
N(eosio.token), N(transfer),
std::make_tuple(from, _self, quantity, std::string(""))
).send();
eos_currency::inline_transfer( from, _self, amount );
accounts.modify( itr, 0, [&]( auto& acnt ) {
acnt.eos_balance = (asset)(eos_currency::token_type(acnt.eos_balance) + amount);
acnt.eos_balance += quantity;
});
}
//@abi action
void withdraw( const account_name to, const asset& a ) {
void withdraw( const account_name to, const asset& quantity ) {
require_auth( to );
eosio_assert( quantity.is_valid(), "invalid quantity" );
eosio_assert( quantity.amount > 0, "must withdraw positive quantity" );
auto itr = accounts.find( to );
eosio_assert(itr != accounts.end(), "unknown account");
auto amount = eos_currency::token_type(a);
accounts.modify( itr, 0, [&]( auto& acnt ) {
acnt.eos_balance = (asset)(eos_currency::token_type(acnt.eos_balance) - amount);
eosio_assert( acnt.eos_balance >= quantity, "insufficient balance" );
acnt.eos_balance -= quantity;
});
eos_currency::inline_transfer( _self, to, amount );
action(
permission_level{ _self, N(active) },
N(eosio.token), N(transfer),
std::make_tuple(_self, to, quantity, std::string(""))
).send();
if( itr->is_empty() ) {
accounts.erase(itr);
......@@ -354,7 +368,7 @@ class dice : public eosio::contract {
// Update winner account balance and game count
auto winner_account = accounts.find(winner_offer.owner);
accounts.modify( winner_account, 0, [&]( auto& acnt ) {
acnt.eos_balance.amount += 2*g.bet.amount;
acnt.eos_balance += 2*g.bet;
acnt.open_games--;
});
......
......@@ -199,7 +199,7 @@ namespace eosio {
friend bool operator==( const asset& a, const asset& b ) {
eosio_assert( a.symbol == b.symbol, "comparison of assets with different symbols is not allowed" );
return a.amount < b.amount;
return a.amount == b.amount;
}
friend bool operator!=( const asset& a, const asset& b ) {
......
......@@ -219,33 +219,34 @@ BOOST_AUTO_TEST_SUITE(dice_tests)
BOOST_FIXTURE_TEST_CASE( dice_test, dice_tester ) try {
set_code(config::system_account_name, eosio_token_wast);
set_abi(config::system_account_name, eosio_token_abi);
create_accounts( {N(eosio.token), N(dice),N(alice),N(bob),N(carol),N(david)}, false);
set_code(N(eosio.token), eosio_token_wast);
set_abi(N(eosio.token), eosio_token_abi);
create_accounts( {N(dice),N(alice),N(bob),N(carol),N(david)}, false);
produce_block();
add_dice_authority(N(alice));
add_dice_authority(N(bob));
add_dice_authority(N(carol));
push_action(N(eosio), N(create), N(eosio), mvo()
("issuer", "eosio")
("maximum_supply", "1000000000000.0000 EOS")
push_action(N(eosio.token), N(create), N(eosio.token), mvo()
("issuer", "eosio.token")
("maximum_supply", "1000000000.0000 EOS")
("can_freeze", "0")
("can_recall", "0")
("can_whitelist", "0")
);
push_action(N(eosio), N(issue), N(eosio), mvo()
push_action(N(eosio.token), N(issue), N(eosio.token), mvo()
("to", "eosio")
("quantity", "1000000.0000 EOS")
("quantity", "1000000000.0000 EOS")
("memo", "")
);
transfer( N(eosio), N(alice), "10000.0000 EOS", "", N(eosio) );
transfer( N(eosio), N(bob), "10000.0000 EOS", "", N(eosio) );
transfer( N(eosio), N(carol), "10000.0000 EOS", "", N(eosio) );
transfer( N(eosio), N(alice), "10000.0000 EOS", "", N(eosio.token) );
transfer( N(eosio), N(bob), "10000.0000 EOS", "", N(eosio.token) );
transfer( N(eosio), N(carol), "10000.0000 EOS", "", N(eosio.token) );
produce_block();
......@@ -283,7 +284,7 @@ BOOST_FIXTURE_TEST_CASE( dice_test, dice_tester ) try {
// Alice tries to bet 1000 EOS (fail)
// secret : a512f6b1b589a8906d574e9de74a529e504a5c53a760f0991a3e00256c027971
BOOST_CHECK_THROW( offer_bet( N(alice), asset::from_string("10000.0000 EOS"),
BOOST_CHECK_THROW( offer_bet( N(alice), asset::from_string("1000.0000 EOS"),
commitment_for("a512f6b1b589a8906d574e9de74a529e504a5c53a760f0991a3e00256c027971")
), fc::exception);
produce_block();
......@@ -384,7 +385,7 @@ BOOST_FIXTURE_TEST_CASE( dice_test, dice_tester ) try {
BOOST_REQUIRE_EQUAL( balance_of(N(alice)), asset::from_string("1.0000 EOS"));
BOOST_REQUIRE_EQUAL(
get_currency_balance(N(eosio), EOS_SYMBOL, N(alice)),
get_currency_balance(N(eosio.token), EOS_SYMBOL, N(alice)),
asset::from_string("10009.0000 EOS")
);
......@@ -396,7 +397,7 @@ BOOST_FIXTURE_TEST_CASE( dice_test, dice_tester ) try {
withdraw( N(alice), asset::from_string("1.0000 EOS"));
BOOST_REQUIRE_EQUAL(
get_currency_balance(N(eosio), EOS_SYMBOL, N(alice)),
get_currency_balance(N(eosio.token), EOS_SYMBOL, N(alice)),
asset::from_string("10010.0000 EOS")
);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册