From 6d6600e1e130029b02be64e6e9fdd7cd4412a5ef Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Fri, 16 Mar 2018 09:41:51 -0400 Subject: [PATCH] Producer pay fixes, token issuing --- contracts/eosio.system/common.hpp | 44 +++++----- contracts/eosio.system/eosio.system.abi | 24 ++++-- contracts/eosio.system/eosio.system.hpp | 109 ++++++++++-------------- contracts/eosio.system/voting.hpp | 22 ++++- contracts/eosiolib/generic_currency.hpp | 9 ++ tests/wasm_tests/eosio.system_tests.cpp | 61 ++++++++++++- 6 files changed, 175 insertions(+), 94 deletions(-) diff --git a/contracts/eosio.system/common.hpp b/contracts/eosio.system/common.hpp index 8282f3620..e614d6159 100644 --- a/contracts/eosio.system/common.hpp +++ b/contracts/eosio.system/common.hpp @@ -13,34 +13,36 @@ namespace eosiosystem { public: static constexpr account_name system_account = SystemAccount; typedef eosio::generic_currency< eosio::token > currency; - typedef typename currency::token_type system_token_type; - static constexpr uint64_t currency_symbol = currency::symbol; // S(4,EOS) - static constexpr uint8_t currency_decimals = currency_symbol & 0xFF; // 4 - static const uint32_t max_inflation_rate = 5; // 5% annual inflation -#warning "change to config parameter" - static constexpr uint32_t producer_repititions = 12; - static constexpr uint32_t blocks_per_cycle = 21 * producer_repititions; - static const uint32_t mseconds_per_day = 24 * 3600 * 1000; - static constexpr uint32_t days_per_4years = 1461; + typedef typename currency::token_type system_token_type; + + static constexpr uint64_t currency_symbol = currency::symbol; // S(4,EOS) + static constexpr uint8_t currency_decimals = currency_symbol & 0xFF; // 4 + static const uint32_t max_inflation_rate = 5; // 5% annual inflation + + static const uint32_t blocks_per_producer = 6; + static const uint32_t seconds_per_day = 24 * 3600; + static constexpr uint32_t days_per_4years = 1461; struct eosio_parameters : eosio::blockchain_parameters { - uint32_t percent_of_max_inflation_rate = 0; // inflation coefficient * 10000 (i.e. inflation in percent * 100) + uint32_t percent_of_max_inflation_rate = 0; uint32_t storage_reserve_ratio = 1000; // ratio * 1000 EOSLIB_SERIALIZE_DERIVED( eosio_parameters, eosio::blockchain_parameters, (percent_of_max_inflation_rate)(storage_reserve_ratio) ) }; struct eosio_global_state : eosio_parameters { - uint64_t total_storage_bytes_reserved = 0; - system_token_type total_storage_stake; - system_token_type payment_per_block = system_token_type(); - system_token_type payment_to_eos_bucket = system_token_type(); - time first_block_time_in_cycle = 0; - time last_bucket_fill_time = 0; - system_token_type eos_bucket = system_token_type(); + uint64_t total_storage_bytes_reserved = 0; + system_token_type total_storage_stake; + system_token_type payment_per_block = system_token_type(); + system_token_type payment_to_eos_bucket = system_token_type(); + time first_block_time_in_cycle = 0; + uint32_t blocks_per_cycle = 0; //6 * 21; + time last_bucket_fill_time = 0; + system_token_type eos_bucket = system_token_type(); EOSLIB_SERIALIZE_DERIVED( eosio_global_state, eosio_parameters, (total_storage_bytes_reserved)(total_storage_stake) - (payment_per_block)(payment_to_eos_bucket)(first_block_time_in_cycle)(last_bucket_fill_time)(eos_bucket) ) + (payment_per_block)(payment_to_eos_bucket)(first_block_time_in_cycle)(blocks_per_cycle) + (last_bucket_fill_time)(eos_bucket) ) }; typedef eosio::singleton global_state_singleton; @@ -56,8 +58,8 @@ namespace eosiosystem { static const uint64_t denom = 10000; uint64_t ret = 0; uint64_t x_power = x; - const uint8_t n = 4; - static constexpr uint64_t ten_power = denom * denom * denom * denom; + const uint64_t n = 4; + static const uint64_t ten_power = denom * denom * denom * denom; for (uint64_t i = 0, p = ten_power; i < n; ++i) { uint64_t factor = x_power * p / (i+1); ret += factor; @@ -65,7 +67,7 @@ namespace eosiosystem { x_power *= x; p /= denom; } - return std::make_pair(ret * denom/ten_power, denom); + return std::make_pair(ret / (denom * denom * denom), denom); } } diff --git a/contracts/eosio.system/eosio.system.abi b/contracts/eosio.system/eosio.system.abi index d965afff2..416e85d93 100644 --- a/contracts/eosio.system/eosio.system.abi +++ b/contracts/eosio.system/eosio.system.abi @@ -92,7 +92,7 @@ {"name":"max_inline_depth", "type":"uint16"}, {"name":"max_inline_action_size", "type":"uint32"}, {"name":"max_generated_transaction_size", "type":"uint32"}, - {"name":"inflation_rate", "type":"uint32"}, + {"name":"percent_of_max_inflation_rate", "type":"uint32"}, {"name":"storage_reserve_ratio", "type":"uint32"} ] },{ @@ -100,16 +100,19 @@ "base": "eosio_parameters", "fields": [ {"name":"total_storage_bytes_reserved", "type":"uint64"}, - {"name":"total_storage_stake", "type":"uint64"} + {"name":"total_storage_stake", "type":"uint64"}, + {"name":"payment_per_block", "type":"uint64"} ] },{ "name": "producer_info", "base": "", "fields": [ - {"name":"owner", "type":"uint64"}, - {"name":"total_votes", "type":"uint128"}, - {"name":"prefs", "type":"eosio_parameters"}, - {"name":"packed_key", "type":"uint8[]"} + {"name":"owner", "type":"uint64"}, + {"name":"total_votes", "type":"uint128"}, + {"name":"prefs", "type":"eosio_parameters"}, + {"name":"packed_key", "type":"uint8[]"}, + {"name":"per_block_payments", "type":"uint64"}, + {"name":"last_claim_time", "type":"uint32"} ] },{ "name": "regproducer", @@ -149,6 +152,12 @@ {"name":"deferred_trx_id", "type":"uint32"}, {"name":"last_unstake", "type":"uint32"} ] + },{ + "name": "claimrewards", + "base": "", + "fields": [ + {"name":"owner", "type":"account_name"} + ] } ], "actions": [{ @@ -172,6 +181,9 @@ },{ "name": "voteproducer", "type": "voteproducer" + },{ + "name": "claimrewards", + "type": "claimrewards" } ], "tables": [ diff --git a/contracts/eosio.system/eosio.system.hpp b/contracts/eosio.system/eosio.system.hpp index 9ccf32b29..7d716f6f5 100644 --- a/contracts/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/eosio.system.hpp @@ -11,21 +11,7 @@ #include namespace eosiosystem { - /* - struct PACKED(producer_key) { - account_name producer_name; - public_key block_signing_key; - EOSLIB_SERIALIZE(producer_key, (producer_name)(block_signing_key)) - }; - - struct PACKED(producer_schedule) { - uint32_t version; - std::vector producers; - - EOSLIB_SERIALIZE(producer_schedule, (version)(producers)) - }; - */ struct PACKED(block_header) { checksum256 previous; time timestamp; @@ -49,9 +35,10 @@ namespace eosiosystem { using system_token_type = typename common::system_token_type; using producers_table = typename pe::producers_table; using global_state_singleton = typename voting::global_state_singleton; - // using mseconds_per_day = typename common::mseconds_per_day; static const uint32_t max_inflation_rate = common::max_inflation_rate; - static const uint32_t mseconds_per_day = common::mseconds_per_day; + static const uint32_t seconds_per_day = common::seconds_per_day; + static const uint32_t num_of_payed_producers = 121; + static const uint32_t slot = 1; ACTION( SystemAccount, nonce ) { eosio::string value; @@ -63,46 +50,24 @@ namespace eosiosystem { } static bool update_cycle(time block_time) { - auto parameters = global_state_singleton::exists() ? global_state_singleton::get() - : common::get_default_parameters(); + auto parameters = global_state_singleton::exists() ? global_state_singleton::get() + : common::get_default_parameters(); if (parameters.first_block_time_in_cycle == 0) { voting::update_elected_producers(block_time); return true; } - - static const time slot = 500; - static const uint32_t slots_per_cycle = common::blocks_per_cycle; - const time delta = block_time - parameters.first_block_time_in_cycle; - uint32_t time_slots = delta / slot; - if (time_slots >= common::blocks_per_cycle) { - time beginning_of_cycle = block_time - (time_slots % slots_per_cycle) * slot; + + static const uint32_t slots_per_cycle = parameters.blocks_per_cycle; + const uint32_t time_slots = block_time - parameters.first_block_time_in_cycle; + if (time_slots >= slots_per_cycle) { + time beginning_of_cycle = block_time - (time_slots % slots_per_cycle); voting::update_elected_producers(beginning_of_cycle); return true; } return false; } - /* - static system_token_type daily_payment(uint32_t percent_of_max_inflation_rate) - { - const system_token_type token_supply = currency::get_total_supply(); - const auto inflation_rate = max_inflation_rate * percent_of_max_inflation_rate; - const auto& inflation_ratio = int_logarithm_one_plus(inflation_rate); - return ((token_supply * inflation_ratio.first * 4) / - (inflation_ratio.second * common::days_per_4years)); - } - static void fill_eos_bucket(time block_time) { - auto parameters = global_state_singleton::get(); - const time delta = block_time - parameters.last_bucket_fill_time; - if (delta >= mseconds_per_day) { - parameters.last_bucket_fill_time = parameters.last_bucket_fill_time + mseconds_per_day; - uint32_t percent = parameters.percent_of_max_inflation_rate - parameters.percent_of_max_inflation_rate / 2; - parameters.eos_bucket += daily_payment(percent); - global_state_singleton::set(parameters); - } - } - */ ACTION(SystemAccount, onblock) { block_header header; @@ -111,7 +76,9 @@ namespace eosiosystem { static void on(const onblock& ob) { // update parameters if it's a new cycle - update_cycle(ob.header.timestamp); + if (update_cycle(ob.header.timestamp)) { + // prints("\nNew Cycle!\n\n"); + } producers_table producers_tbl(SystemAccount, SystemAccount); account_name producer = ob.header.producer; auto parameters = global_state_singleton::exists() ? global_state_singleton::get() @@ -119,36 +86,44 @@ namespace eosiosystem { const system_token_type block_payment = parameters.payment_per_block; const auto* prod = producers_tbl.find(producer); // This check is needed when everything works - // eosio_assert(prod != nullptr, "something wrong here"); + // eosio_assert(prod != nullptr, "something wrong here"); if (prod != nullptr) { + // prints("Producer found\n"); producers_tbl.update(*prod, 0, [&](auto& p) { + // printi((uint64_t)p.per_block_payments.quantity); + // prints("\n"); p.per_block_payments += block_payment; + // printi((uint64_t)p.per_block_payments.quantity); + // prints("\n"); p.last_produced_block_time = ob.header.timestamp; }); } - - const uint32_t num_of_payments = (ob.header.timestamp - parameters.last_bucket_fill_time) / 500; + + // prints("payments to eos bucket\n"); + const uint32_t num_of_payments = ob.header.timestamp - parameters.last_bucket_fill_time; const system_token_type to_eos_bucket = num_of_payments * parameters.payment_to_eos_bucket; + // printi(to_eos_bucket.quantity); + // prints("\n"); parameters.last_bucket_fill_time = ob.header.timestamp; parameters.eos_bucket += to_eos_bucket; global_state_singleton::set(parameters); } - ACTION(SystemAccount, claim_rewards) { + ACTION(SystemAccount, claimrewards) { account_name owner; - time claim_time; - EOSLIB_SERIALIZE(claim_rewards, (owner)(claim_time)) + EOSLIB_SERIALIZE(claimrewards, (owner)) }; - static void on(const claim_rewards& cr) { + static void on(const claimrewards& cr) { + require_auth(cr.owner); + eosio_assert(current_sender() == account_name(), "claimrewards can not be part of a deferred transaction"); producers_table producers_tbl(SystemAccount, SystemAccount); const auto* prod = producers_tbl.find(cr.owner); - eosio_assert(prod != nullptr, "account name not proucer list"); + eosio_assert(prod != nullptr, "account name not producer list"); eosio_assert(prod->active(), "producer is not active"); - const time ctime = cr.claim_time; if (prod->last_rewards_claim > 0) { - eosio_assert(ctime >= prod->last_rewards_claim + mseconds_per_day, "already claimed rewards today"); + eosio_assert(now() >= prod->last_rewards_claim + seconds_per_day, "already claimed rewards within a day"); } system_token_type rewards = prod->per_block_payments; auto idx = producers_tbl.template get_index(); @@ -156,13 +131,15 @@ namespace eosiosystem { bool is_among_payed_producers = false; uint64_t total_producer_votes = 0; - for (uint32_t i = 0; i < 121; ++i) { + uint32_t n = 0; + while (n < num_of_payed_producers) { if (!is_among_payed_producers) { if (itr->owner == cr.owner) is_among_payed_producers = true; } if (itr->active()) { total_producer_votes += itr->total_votes; + ++n; } if (itr == idx.begin()) { break; @@ -170,14 +147,20 @@ namespace eosiosystem { --itr; } - if (is_among_payed_producers) { - const system_token_type eos_bucket = global_state_singleton::get_or_default().eos_bucket; - rewards += (uint64_t(prod->total_votes) * eos_bucket) / total_producer_votes; + if (is_among_payed_producers && total_producer_votes > 0) { + if (global_state_singleton::exists()) { + auto parameters = global_state_singleton::get(); + auto share_of_eos_bucket = (uint64_t(prod->total_votes) * parameters.eos_bucket) / total_producer_votes; + rewards += share_of_eos_bucket; + parameters.eos_bucket -= share_of_eos_bucket; + global_state_singleton::set(parameters); + } } producers_tbl.update(*prod, 0, [&](auto& p) { - p.last_rewards_claim = ctime; - p.per_block_payments = system_token_type(); + p.last_rewards_claim = now(); + p.per_block_payments.quantity = 0; }); + currency::inline_transfer(cr.owner, SystemAccount, rewards, "producer claiming rewards"); } @@ -192,7 +175,7 @@ namespace eosiosystem { typename voting::voteproducer, typename voting::unstake_vote_deferred, onblock, - claim_rewards, + claimrewards, nonce>( code, act) ) { eosio::print("Unexpected action: ", eosio::name(act), "\n"); eosio_assert( false, "received unexpected action"); diff --git a/contracts/eosio.system/voting.hpp b/contracts/eosio.system/voting.hpp index 78942d61d..a0419c3a2 100644 --- a/contracts/eosio.system/voting.hpp +++ b/contracts/eosio.system/voting.hpp @@ -39,6 +39,7 @@ namespace eosiosystem { using global_state_singleton = typename common::global_state_singleton; static const uint32_t max_inflation_rate = common::max_inflation_rate; + // static const uint32_t blocks_per_cycle = common::blocks_per_cycle; static constexpr uint32_t max_unstake_requests = 10; static constexpr uint32_t unstake_pay_period = 7*24*3600; // one per week static constexpr uint32_t unstake_payments = 26; // during 26 weeks @@ -256,8 +257,21 @@ namespace eosiosystem { static system_token_type payment_per_block(uint32_t percent_of_max_inflation_rate) { const system_token_type token_supply = currency::get_total_supply(); + /* + prints("token_supply\n"); + printi(token_supply.quantity); + prints("\ntoken_supply\n"); + */ const auto inflation_rate = max_inflation_rate * percent_of_max_inflation_rate; const auto& inflation_ratio = int_logarithm_one_plus(inflation_rate); + /* + printi(inflation_rate); + prints("\n"); + printi(inflation_ratio.first); + prints("/"); + printi(inflation_ratio.second); + prints("\n"); + */ return (token_supply * inflation_ratio.first) / (inflation_ratio.second * blocks_per_year); } @@ -356,14 +370,16 @@ namespace eosiosystem { auto other_half_of_percentage = parameters.percent_of_max_inflation_rate - half_of_percentage; parameters.payment_per_block = payment_per_block(half_of_percentage); parameters.payment_to_eos_bucket = payment_per_block(other_half_of_percentage); + parameters.blocks_per_cycle = 6 * schedule.producers.size(); if ( parameters.max_storage_size < parameters.total_storage_bytes_reserved ) { parameters.max_storage_size = parameters.total_storage_bytes_reserved; } - + + auto issue_quantity = parameters.blocks_per_cycle * (parameters.payment_per_block + parameters.payment_to_eos_bucket); + currency::inline_issue(SystemAccount, issue_quantity); set_blockchain_parameters(¶meters); - - global_state_singleton::set( parameters ); + global_state_singleton::set(parameters); } ACTION( SystemAccount, unstake_vote_deferred ) { diff --git a/contracts/eosiolib/generic_currency.hpp b/contracts/eosiolib/generic_currency.hpp index e3db83bbb..59bfce972 100644 --- a/contracts/eosiolib/generic_currency.hpp +++ b/contracts/eosiolib/generic_currency.hpp @@ -21,6 +21,9 @@ namespace eosio { ACTION( code, issue ) { typedef action_meta meta; + + issue() { } + issue(account_name a, asset q): to(a), quantity(q) { } account_name to; asset quantity; @@ -134,6 +137,12 @@ namespace eosio { act.send(); } + static void inline_issue(account_name to, token_type quantity) + { + action act(permission_level(code, N(active)), issue(to, asset(quantity))); + act.send(); + } + static token_type get_total_supply() { stats t( code, code ); auto ptr = t.find( symbol ); diff --git a/tests/wasm_tests/eosio.system_tests.cpp b/tests/wasm_tests/eosio.system_tests.cpp index 03e6dd015..4714f37c5 100644 --- a/tests/wasm_tests/eosio.system_tests.cpp +++ b/tests/wasm_tests/eosio.system_tests.cpp @@ -510,7 +510,7 @@ static fc::variant producer_parameters_example(int n) { ("max_inline_depth", 4 + n) ("max_inline_action_size", 4096 + n) ("max_generated_transaction_size", 64*1024 + n) - ("inflation_rate", 1050 + n) + ("percent_of_max_inflation_rate", 50 + n) ("storage_reserve_ratio", 100 + n); } @@ -672,5 +672,64 @@ BOOST_FIXTURE_TEST_CASE( vote_for_producer, eosio_system_tester ) try { } FC_LOG_AND_RETHROW() +BOOST_FIXTURE_TEST_CASE(producer_pay, eosio_system_tester) try { + issue( "alice", "100000.0000 EOS", config::system_account_name ); + fc::variant params = producer_parameters_example(50); + vector key = fc::raw::pack(get_public_key(N(alice), "active")); + + // 1 block produced + BOOST_REQUIRE_EQUAL(success(), push_action(N(alice), N(regproducer), mvo() + ("producer", "alice") + ("producer_key", key ) + ("prefs", params) + ) + ); + + auto prod = get_producer_info( "alice" ); + BOOST_REQUIRE_EQUAL(N(alice), prod["owner"].as_uint64()); + BOOST_REQUIRE_EQUAL(0, prod["total_votes"].as_uint64()); + REQUIRE_EQUAL_OBJECTS(params, prod["prefs"]); + BOOST_REQUIRE_EQUAL(string(key.begin(), key.end()), to_string(prod["packed_key"])); + + issue("bob", "2000.0000 EOS", config::system_account_name); + + // bob makes stake + // 1 block produced + BOOST_REQUIRE_EQUAL(success(), push_action(N(bob), N(delegatebw), mvo() + ("from", "bob") + ("receiver", "bob") + ("stake_net", "11.0000 EOS") + ("stake_cpu", "00.1111 EOS") + ("stake_storage", "0.0000 EOS") + ) + ); + REQUIRE_EQUAL_OBJECTS(simple_voter("bob", "11.1111 EOS", last_block_time()), get_voter_info("bob")); + + // bob votes for alice + // 1 block produced + BOOST_REQUIRE_EQUAL(success(), push_action(N(bob), N(voteproducer), mvo() + ("voter", "bob") + ("proxy", name(0).to_string()) + ("producers", vector{ N(alice) }) + ) + ); + + produce_blocks(10); + prod = get_producer_info("alice"); + BOOST_REQUIRE(prod["per_block_payments"].as_uint64() > 0); + + // BOOST_REQUIRE_EQUAL(6 * 3925, prod["per_block_payments"].as_uint64()); + + BOOST_REQUIRE_EQUAL(success(), push_action(N(alice), N(claimrewards), mvo() + ("owner", "alice") + ) + ); + + prod = get_producer_info("alice"); + BOOST_REQUIRE_EQUAL(0, prod["per_block_payments"].as_uint64()); + + } FC_LOG_AND_RETHROW() + + BOOST_AUTO_TEST_SUITE_END() -- GitLab