EOSIO / eos

An open source smart contract platform
https://developers.eos.io/manuals/eos
MIT License
11.28k stars 3.6k forks source link

A question with assert_recover_key #7038

Closed HeNanYuanWeitao closed 5 years ago

HeNanYuanWeitao commented 5 years ago

I use eosjs-ecc to signature better's seed:699daf496f8c558a53002db60c1e731acbc7d4071c439d57411a952bd6b17ef1, with the private key: "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3", then I get the signature:"SIG_K1_K3xttLEcS1nFx9gDiHLT47AV6U4m9A3wyugHk5wVXpU8uPJgbuucqjSyp5z3iq4mUe66A2sGpQr46DBkVAYwU4Co7rSQR2", but an error has occurred when I call the interface "assert_recover_key " to verfify: " Error 3230001: Crypto API Exception". This is the log: // warn 2019-04-01T02:23:05.288 thread-0 wabt.hpp:613 translate_one ] misaligned const reference 390035 error 2019-04-01T02:23:05.288 thread-0 http_plugin.cpp:585 handle_exception ] FC Exception encountered while processing chain.push_transaction 390036 debug 2019-04-01T02:23:05.288 thread-0 http_plugin.cpp:586 handle_exception ] Exception Details: 3230001 crypto_api_exception: Crypto API Exception 390037 Error expected key different than recovered key 390038 {} 390039 thread-0 wasm_interface.cpp:705 assert_recover_key 390040 pending console output: public key: 390041 sig: 390042 seed: i<9d>?Io<8c>U<8a>S 390043 bet_id: 7366431540318193527 390044 {"console":"public key: \nsig: \nseed: i<9d>?Io<8c>U<8a>S\nbet_id: 7366431540318193527"} 390045 thread-0 apply_context.cpp:72 exec_one // you can see that only bet_id can print to console, I don't know why!!! the code of signature as follow: let signature = ecc.sign(res.rows[0].seed, '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3') console.log("seed: " + res.rows[0].seed) console.log("signature: " + signature)

let verified = ecc.verify(signature, res.rows[0].seed, 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV','utf8',false) console.log("verified: " + verified) and the log is: seed: 699daf496f8c558a53002db60c1e731acbc7d4071c439d57411a952bd6b17ef1 signature: SIG_K1_K3xttLEcS1nFx9gDiHLT47AV6U4m9A3wyugHk5wVXpU8uPJgbuucqjSyp5z3iq4mUe66A2sGpQr46DBkVAYwU4Co7rSQR2 verified:true

Who can help me!!! My smart contract as follow: /*****/

include

include

include

include <eosiolib/eosio.hpp>

include <eosiolib/time.hpp>

include <eosiolib/asset.hpp>

include <eosiolib/contract.hpp>

include <eosiolib/types.hpp>

include <eosiolib/transaction.hpp>

include <eosiolib/crypto.h>

include <boost/algorithm/string.hpp>

include

using eosio::asset; using eosio::permission_level; using eosio::action; using eosio::print; using eosio::name; using eosio::unpack_action_data; using eosio::symbol_type; using eosio::transaction; using eosio::time_point_sec;

class[[eosio::contract]] eosbetdice : public eosio::contract{ public: const uint32_t TWO_MINUTES = 2 * 60; const uint64_t MINBET = 1000; const uint64_t HOUSEEDGE_times10000 = 200; const uint64_t HOUSEEDGE_REF_times10000 = 150; const uint64_t REFERRER_REWARD_times10000 = 50;

const uint64_t BETID_ID = 1;
const uint64_t TOTALAMTBET_ID = 2;
const uint64_t TOTALAMTWON_ID = 3;
const uint64_t LIABILITIES_ID = 4;

eosbetdice(account_name self) :eosio::contract(self),
    activebets(_self, _self),
    globalvars(_self, _self),
    gamelog(_self, _self),
    randkeys(_self, _self)
{}

[[eosio::action]]
void initcontract(public_key randomness_key) {

    require_auth(N(eosbetcasino));

    auto globalvars_itr = globalvars.begin();
    eosio_assert(globalvars_itr == globalvars.end(), "Contract is init");

    globalvars.emplace(_self, [&](auto& g) {
        g.id = BETID_ID;
        g.val = 0;
    });

    globalvars.emplace(_self, [&](auto& g) {
        g.id = TOTALAMTBET_ID;
        g.val = 0;
    });

    globalvars.emplace(_self, [&](auto& g) {
        g.id = TOTALAMTWON_ID;
        g.val = 0;
    });

    globalvars.emplace(_self, [&](auto& g) {
        g.id = LIABILITIES_ID;
        g.val = 0;
    });

    randkeys.emplace(_self, [&](auto & k) {
    k.id = 1;
    k.key = randomness_key;
    });
}

[[eosio::action]]
void newrandkey(public_key randomness_key) {

    require_auth(N(eosbetcasino));

    auto rand_itr = randkeys.begin();
    randkeys.modify(rand_itr, _self, [&](auto& k) {
        k.key = randomness_key;
    });
}

[[eosio::action]]
void suspendbet(const uint64_t bet_id) {

    require_auth(N(eosbetcasino));

    auto activebets_itr = activebets.find(bet_id);
    eosio_assert(activebets_itr != activebets.end(), "No bet exists");

    std::string bettor_str = name_to_string(activebets_itr->bettor);

    decrement_liabilities(activebets_itr->bet_amt);

    action(
        permission_level{ _self, N(active) },
        N(eosio.token),
        N(transfer),
        std::make_tuple(
            _self,
            activebets_itr->bettor,
            asset(activebets_itr->bet_amt, symbol_type(S(4, SYS))),
            bettor_str
        )
    ).send();

    activebets.erase(activebets_itr);
}

void transfer(uint64_t sender, uint64_t receiver) {

    auto transfer_data = unpack_action_data<st_transfer>();

    if (transfer_data.from == _self || transfer_data.from == N(eosbetcasino)) {
        return;
    }

    eosio_assert(transfer_data.quantity.is_valid(), "Invalid asset");

    const uint64_t your_bet_amount = (uint64_t)transfer_data.quantity.amount;
    eosio_assert(MINBET <= your_bet_amount, "Must bet greater than min");

    increment_liabilities_bet_id(your_bet_amount);

    std::string roll_str;
    std::string ref_str;
    std::string seed_str;

    const std::size_t first_break = transfer_data.memo.find("-");
    roll_str = transfer_data.memo.substr(0, first_break);

    if (first_break != std::string::npos) {

        const std::string after_first_break = transfer_data.memo.substr(first_break + 1);
        const std::size_t second_break = after_first_break.find("-");

        if (second_break != std::string::npos) {

            ref_str = after_first_break.substr(0, second_break);
            seed_str = after_first_break.substr(second_break + 1);
        }
        else {

            ref_str = after_first_break;
            seed_str = std::string("");
        }
    }
    else {
        ref_str = std::string("");
        seed_str = std::string("");
    }

    account_name referral = N(eosbetcasino);

    const account_name possible_ref = eosio::string_to_name(ref_str.c_str());
    uint64_t house_edge = HOUSEEDGE_times10000;

    if (possible_ref != _self && possible_ref != transfer_data.from && is_account(possible_ref)) {
        referral = possible_ref;
        house_edge = HOUSEEDGE_REF_times10000;
    }

    const uint64_t roll_under = std::stoull(roll_str, 0, 10);
    eosio_assert(roll_under >= 2 && roll_under <= 96, "Roll must be >= 2, <= 96.");

    const uint64_t your_win_amount = (your_bet_amount * get_payout_mult_times10000(roll_under, house_edge) / 10000) - your_bet_amount;
    eosio_assert(your_win_amount <= get_max_win(), "Bet less than max");

    checksum256 user_seed_hash;
    sha256((char *)&seed_str, seed_str.length(), &user_seed_hash);

    auto s = read_transaction(nullptr, 0);
    char *tx = (char *)malloc(s);
    read_transaction(tx, s);
    checksum256 tx_hash;
    sha256(tx, s, &tx_hash);

    st_seeds seeds;
    seeds.seed1 = user_seed_hash;
    seeds.seed2 = tx_hash;

    checksum256 seed_hash;
    sha256((char *)&seeds.seed1, sizeof(seeds.seed1) * 2, &seed_hash);

    const uint64_t bet_id = ((uint64_t)tx_hash.hash[0] << 56) + ((uint64_t)tx_hash.hash[1] << 48) + ((uint64_t)tx_hash.hash[2] << 40) + ((uint64_t)tx_hash.hash[3] << 32) + ((uint64_t)tx_hash.hash[4] << 24) + ((uint64_t)tx_hash.hash[5] << 16) + ((uint64_t)tx_hash.hash[6] << 8) + (uint64_t)tx_hash.hash[7];

    activebets.emplace(_self, [&](auto& bet) {
        bet.id = bet_id;
        bet.bettor = transfer_data.from;
        bet.referral = referral;
        bet.bet_amt = your_bet_amount;
        bet.roll_under = roll_under;
        bet.seed = seed_hash;
        bet.bet_time = time_point_sec(now());
    });
}

[[eosio::action]]
void resolvebet(const uint64_t bet_id, signature sig) {

    //require_auth2(N(eosbetcasino), N(random));
    require_auth(N(eosbetcasino));

    auto activebets_itr = activebets.find(bet_id);
    eosio_assert(activebets_itr != activebets.end(), "Bet doesn't exist");

    auto key_entry = randkeys.get(1);
    public_key rand_signing_key = key_entry.key;

    eosio::print("public key: ", std::string(rand_signing_key.data).c_str(), "\n");
    eosio::print("sig: ", std::string(std::begin(sig.data), std::end(sig.data)).c_str(), "\n");
    eosio::print("seed: ", std::string(std::begin(activebets_itr->seed.hash), std::end(activebets_itr->seed.hash)).c_str(), "\n");
    eosio::print("bet_id: ", bet_id);
    assert_recover_key(&activebets_itr->seed, (const char *)&sig, sizeof(sig), (const char *)&rand_signing_key, sizeof(rand_signing_key));

    checksum256 random_num_hash;
    sha256((char *)&sig, sizeof(sig), &random_num_hash);

    const uint64_t random_roll = ((random_num_hash.hash[0] + random_num_hash.hash[1] + random_num_hash.hash[2] + random_num_hash.hash[3] + random_num_hash.hash[4] + random_num_hash.hash[5] + random_num_hash.hash[6] + random_num_hash.hash[7]) % 100) + 1;

    /*****************************************
    auto mixd = tapos_block_prefix() * tapos_block_num() + activebets_itr->bettor + bet_id - current_time();
    const char* mixedChar = reinterpret_cast<const char*>(&mixd);
    checksum256 result;
    sha256((char*)mixedChar, sizeof(mixedChar), &result);
    const uint64_t random_roll = (*(uint64_t*)(&result.hash[0]) + *(uint64_t*)(&result.hash[8]) + *(uint64_t*)(&result.hash[16]) + *(uint64_t*)(&result.hash[24])) % 100 + 1;
    *****************************************/

    eosio::print("random roll: ", random_roll, "\n");

    uint64_t edge = HOUSEEDGE_times10000;
    uint64_t ref_reward = 0;
    uint64_t payout = 0;
    if (activebets_itr->referral != N(eosbetcasino)) {
        edge = HOUSEEDGE_REF_times10000;
        ref_reward = activebets_itr->bet_amt * REFERRER_REWARD_times10000 / 10000;
    }

    if (random_roll < activebets_itr->roll_under) {
        payout = (activebets_itr->bet_amt * get_payout_mult_times10000(activebets_itr->roll_under, edge)) / 10000;
    }

    eosio::print("payout: ", payout, "\n");

    increment_game_stats(activebets_itr->bet_amt, payout);
    decrement_liabilities(activebets_itr->bet_amt);

    if (payout > 0) {
        action(
            permission_level{ _self, N(active) },
            N(eosio.token),
            N(transfer),
            std::make_tuple(
                _self,
                activebets_itr->bettor,
                asset(payout, symbol_type(S(4, SYS))),
                std::string("Bet id: ") + std::to_string(bet_id) + std::string(" -- Winner! Play: dice.eosbet.io")
            )
        ).send();
        eosio::print("transfer OK!!!\n");
    }

    gamelog.emplace(_self, [&](auto& log) {
        log.id = bet_id;
        log.bettor = activebets_itr->bettor;
        log.referral = activebets_itr->referral;
        log.bet_amt = activebets_itr->bet_amt;
        log.payout = payout;
        log.roll_under = activebets_itr->roll_under;
        log.random_roll = random_roll;
        log.bet_time = activebets_itr->bet_time;
    });

    transaction ref_tx{};

    ref_tx.actions.emplace_back(
        permission_level{ _self, N(active) },
        _self,
        N(betreceipt),
        std::make_tuple(
            bet_id,
            activebets_itr->bettor,
            N(eosio.token),
            asset(activebets_itr->bet_amt, symbol_type(S(4, SYS))),
            asset(payout, symbol_type(S(4, SYS))),
            activebets_itr->seed,
            sig,
            activebets_itr->roll_under,
            random_roll
        )
    );

    if (ref_reward > 0) {

        ref_tx.actions.emplace_back(
            permission_level{ _self, N(active) },
            N(eosio.token),
            N(transfer),
            std::make_tuple(
                _self,
                N(safetransfer),
                asset(ref_reward, symbol_type(S(4, SYS))),
                name_to_string(activebets_itr->referral) + std::string(" Bet id: ") + std::to_string(bet_id) + std::string(" -- Referral reward! Play: dice.eosbet.io")
            )
        );
    }

    ref_tx.delay_sec = 5;
    ref_tx.send(bet_id, _self);

    //airdrop_tokens(bet_id, activebets_itr->bet_amt, activebets_itr->bettor);
    activebets.erase(activebets_itr);
}

[[eosio::action]]
void betreceipt(
    uint64_t bet_id,
    account_name bettor,
    account_name amt_contract,
    asset bet_amt,
    asset payout,
    checksum256 seed,
    signature sig,
    uint64_t roll_under,
    uint64_t random_roll
) {

    require_auth(N(eosbetcasino));
    require_recipient(bettor);
}

[[eosio::action]]
void refundbet(const uint64_t bet_id) {

    //require_auth2(N(eosbetcasino), N(random));
    require_auth(N(eosbetcasino));

    auto activebets_itr = activebets.find(bet_id);

    eosio_assert(activebets_itr != activebets.end(), "Game doesn't exist");

    const time_point_sec bet_time = activebets_itr->bet_time;

    eosio_assert(time_point_sec(now() - TWO_MINUTES) > bet_time, "Wait 10 minutes");

    decrement_liabilities(activebets_itr->bet_amt);

    action(
        permission_level{ _self, N(active) },
        N(eosio.token),
        N(transfer),
        std::make_tuple(
            _self,
            N(safetransfer),
            asset(activebets_itr->bet_amt, symbol_type(S(4, SYS))),
            name_to_string(activebets_itr->bettor) + std::string(" Bet id: ") + std::to_string(bet_id) + std::string(" -- REFUND. Sorry for the inconvenience.")
        )
    ).send();

    activebets.erase(activebets_itr);
}

[[eosio::action]]
void deletelog(const uint64_t bet_id) {

    require_auth(N(eosbetcasino));

    auto resultlog_itr = gamelog.find(bet_id);
    eosio_assert(resultlog_itr != gamelog.end(), "No betlog exists");

    gamelog.erase(resultlog_itr);
}

private: struct[[eosio::table]] bet{ uint64_t id; account_name bettor; account_name referral; uint64_t bet_amt; uint64_t roll_under; checksum256 seed; time_point_sec bet_time;

    uint64_t        primary_key() const { return id; }

    EOSLIB_SERIALIZE(bet, (id)(bettor)(referral)(bet_amt)(roll_under)(seed)(bet_time))
};

typedef eosio::multi_index< N(activebets), bet> bets_index;

struct[[eosio::table]] globalvar{
    uint64_t        id;
uint64_t        val;

uint64_t        primary_key() const { return id; }

EOSLIB_SERIALIZE(globalvar, (id)(val));
};

typedef eosio::multi_index< N(globalvars), globalvar> globalvars_index;

struct[[eosio::table]] log{
    uint64_t        id;
account_name    bettor;
account_name    referral;
uint64_t        bet_amt;
uint64_t        payout;
uint64_t        roll_under;
uint64_t        random_roll;
time_point_sec  bet_time;

uint64_t        primary_key() const { return id; }

EOSLIB_SERIALIZE(log, (id)(bettor)(referral)(bet_amt)(payout)(roll_under)(random_roll)(bet_time))
};

typedef eosio::multi_index< N(gamelog), log> log_index;

struct[[eosio::table]] randkey {
    uint64_t        id;
    public_key      key;

    uint64_t        primary_key() const { return id; }
};

typedef eosio::multi_index< N(randkeys), randkey > randkeys_index;

struct[[eosio::table]] account{
    asset    balance;

    uint64_t primary_key() const { return balance.symbol.name(); }
};

typedef eosio::multi_index<N(accounts), account> accounts;

struct st_transfer {
    account_name  from;
    account_name  to;
    asset         quantity;
    std::string   memo;
};

struct st_seeds {
    checksum256     seed1;
    checksum256     seed2;
};

bets_index          activebets;
globalvars_index    globalvars;
log_index           gamelog;
randkeys_index      randkeys;

std::string name_to_string(uint64_t acct_int) const {
    static const char* charmap = ".12345abcdefghijklmnopqrstuvwxyz";

    std::string str(13,'.');

    uint64_t tmp = acct_int;

    for (uint32_t i = 0; i <= 12; ++i) {
        char c = charmap[tmp & (i == 0 ? 0x0f : 0x1f)];
        str[12 - i] = c;
        tmp >>= (i == 0 ? 4 : 5);
    }

    boost::algorithm::trim_right_if(str, [](char c) { return c == '.'; });
    return str;
}

void increment_liabilities_bet_id(const uint64_t bet_amt) {

    auto globalvars_itr = globalvars.find(BETID_ID);

    globalvars.modify(globalvars_itr, _self, [&](auto& g) {
        g.val++;
    });

    globalvars_itr = globalvars.find(LIABILITIES_ID);

    globalvars.modify(globalvars_itr, _self, [&](auto& g) {
        g.val += bet_amt;
    });
}

void increment_game_stats(const uint64_t bet_amt, const uint64_t won_amt) {

    auto globalvars_itr = globalvars.find(TOTALAMTBET_ID);

    globalvars.modify(globalvars_itr, _self, [&](auto& g) {
        g.val += bet_amt;
    });

    if (won_amt > 0) {
        globalvars_itr = globalvars.find(TOTALAMTWON_ID);

        globalvars.modify(globalvars_itr, _self, [&](auto& g) {
            g.val += won_amt;
        });
    }
}

void decrement_liabilities(const uint64_t bet_amt) {
    auto globalvars_itr = globalvars.find(LIABILITIES_ID);

    globalvars.modify(globalvars_itr, _self, [&](auto& g) {
        g.val -= bet_amt;
    });
}
/*
void airdrop_tokens(const uint64_t bet_id, const uint64_t bet_amt, const account_name bettor){
uint64_t drop_amt = (1 * bet_amt) / 30;

const uint64_t bet_token_balance = get_token_balance( N(betdividends), symbol_type(S(4, BET)) );

if (bet_token_balance == 0){
return;
}
else if (bet_token_balance < drop_amt){
drop_amt = bet_token_balance;
}
action(
permission_level{_self, N(active)},
N(betdividends),
N(transfer),
std::make_tuple(
_self,
bettor,
asset(drop_amt, symbol_type(S(4, BET))),
std::string("Bet id: ") + std::to_string(bet_id) + std::string(" -- Enjoy airdrop! Play: dice.eosbet.io")
)
).send();
}
*/
uint64_t get_token_balance(const account_name token_contract, const symbol_type& token_type) const {

    accounts from_accounts(token_contract, _self);

    const auto token_name = token_type.name();
    auto my_account_itr = from_accounts.find(token_name);
    if (my_account_itr == from_accounts.end()) {
        return 0;
    }
    const asset my_balance = my_account_itr->balance;
    return (uint64_t)my_balance.amount;
}

uint64_t get_payout_mult_times10000(const uint64_t roll_under, const uint64_t house_edge_times_10000) const {

    return ((10000 - house_edge_times_10000) * 100) / (roll_under - 1);
}

uint64_t get_max_win() const {
    const uint64_t eos_balance = get_token_balance(N(eosio.token), symbol_type(S(4, SYS)));

    auto liabilities_struct = globalvars.get(LIABILITIES_ID);
    const uint64_t liabilities = liabilities_struct.val;

    return (eos_balance - liabilities) / 25;
}

};

define EOSIO_ABI_EX( TYPE, MEMBERS ) \

extern "C" { \ void apply( uint64_t receiver, uint64_t code, uint64_t action ) { \ auto self = receiver; \ if( code == self || code == N(eosio.token)) { \ if( action == N(transfer)){ \ eosio_assert( code == N(eosio.token), "Must transfer SYS"); \ } \ TYPE thiscontract( self ); \ switch( action ) { \ EOSIO_API( TYPE, MEMBERS ) \ } \ / does not allow destructor of thiscontract to run: eosio_exit(0); / \ } \ } \ }

EOSIO_ABI_EX(eosbetdice, (initcontract) (newrandkey) (resolvebet) (refundbet) (transfer) (betreceipt) (suspendbet) (deletelog) ) /*****/

HeNanYuanWeitao commented 5 years ago

I modify my code: std::string to_hex(const char d, uint32_t s) { std::string r; const char to_hex = "0123456789abcdef"; uint8_t c = (uint8_t)d; for (uint32_t i = 0; i < s; ++i) (r += to_hex[(c[i] >> 4)]) += to_hex[(c[i] & 0x0f)]; return r; } eosio::print("seed: ", to_hex((char*)activebets_itr->seed.hash, sizeof(activebets_itr->seed.hash)).c_str(), "\n"); And then it can print the seed: warn 2019-04-01T07:36:27.953 thread-0 wabt.hpp:613 translate_one ] misaligned const reference 427693 error 2019-04-01T07:36:27.954 thread-0 http_plugin.cpp:585 handle_exception ] FC Exception encountered while processing chain.push_transaction 427694 debug 2019-04-01T07:36:27.954 thread-0 http_plugin.cpp:586 handle_exception ] Exception Details: 3230001 crypto_api_exception: Crypto API Exception 427695 Error expected key different than recovered key 427696 {} 427697 thread-0 wasm_interface.cpp:705 assert_recover_key 427698 pending console output: public key: 0002c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf 427699 sig: 001f4291539c1067c1e2dc1fc71083cb2d8ab24149e82367a62d67bc47f8dd0829960c93e45a9cda5c405425e65a92dd5e1853f02320400c386f2a4ebfc29ecb15b1 427700 seed: 699daf496f8c558a53002db60c1e731acbc7d4071c439d57411a952bd6b17ef1 427701 bet_id: 7366431540318193527 427702 {"console":"public key: 0002c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf\nsig: 001f4291539c1067c1e2dc1fc71083cb2d8ab24149e82367a62d67bc47f8dd0829960c93e45a9cd a5c405425e65a92dd5e1853f02320400c386f2a4ebfc29ecb15b1\nseed: 699daf496f8c558a53002db60c1e731acbc7d4071c439d57411a952bd6b17ef1\nbet_id: 7366431540318193527"}

It will be seen by encode with base58 about the public key and the signature, I will try it. But why assert_recover_key throw that exception if the public key and signature is correct.

HeNanYuanWeitao commented 5 years ago

I get the hex number:"02C0DED2BC1F1305FB0FAAC5E6C03EE3A1924234985427B6167CA569D13DF435CFEB05F9D2" by decode the public:"6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", it is different with the result by my code: eosio::print("public key: ", to_hex((char*)rand_signing_key.data, sizeof(rand_signing_key.data)).c_str(), "\n"); 427698 pending console output: public key: 0002c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf I get table randkeys: vm:~/eosio.cdt$ cleos get table eosbetcasino eosbetcasino randkeys { "rows": [{ "id": 1, "key": "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" } ], "more": false } I don't know why!!