From 6d2f5c4144b6123bbdbeef8174adc34c0e674097 Mon Sep 17 00:00:00 2001 From: Zach <34947245+kj4ezj@users.noreply.github.com> Date: Thu, 3 Jan 2019 15:30:20 -0500 Subject: [PATCH 01/23] Created Pull Request Template (#161) Other: Created pull request template --- .github/PULL_REQUEST_TEMPLATE.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..364491293 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,17 @@ + + + +## Change Description + + + +## Deployment Changes + + + +## API Changes + + + +## Documentation Additions + From 20ce75f674e6c5c3c461fb373ce68c340119dcff Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Thu, 10 Jan 2019 18:21:37 -0500 Subject: [PATCH 02/23] Add inline dummy actions to sell and buy rex for bookkeeping --- .../include/eosio.system/eosio.system.hpp | 1 + contracts/eosio.system/src/rex.cpp | 36 +++++--- contracts/eosio.system/src/voting.cpp | 1 - tests/eosio.system_tester.hpp | 84 +++++++++++++++++++ tests/eosio.system_tests.cpp | 62 +++++--------- 5 files changed, 133 insertions(+), 51 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index ff7758a2c..cd2dab444 100755 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -301,6 +301,7 @@ namespace eosiosystem { static constexpr eosio::name names_account{"eosio.names"_n}; static constexpr eosio::name saving_account{"eosio.saving"_n}; static constexpr eosio::name rex_account{"eosio.rex"_n}; + static constexpr eosio::name null_account{"eosio.null"_n}; static constexpr symbol ramcore_symbol = symbol(symbol_code("RAMCORE"), 4); static constexpr symbol ram_symbol = symbol(symbol_code("RAM"), 0); static constexpr symbol rex_symbol = symbol(symbol_code("REX"), 4); diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index db4360b0f..99e57054c 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -59,6 +59,10 @@ namespace eosiosystem { const asset delta_rex_stake = add_to_rex_balance( from, amount, rex_received ); runrex(2); update_rex_account( from, asset( 0, core_symbol() ), delta_rex_stake ); + // dummy action added so that amount of REX tokens purchased shows up in action trace + dispatch_inline( null_account, "buyresult"_n, + std::vector{ { from, active_permission } }, + std::make_tuple( rex_received ) ); } /** @@ -75,7 +79,7 @@ namespace eosiosystem { check( from_net.symbol == core_symbol() && from_cpu.symbol == core_symbol(), "asset must be core token" ); check( (0 <= from_net.amount) && (0 <= from_cpu.amount) && (0 < from_net.amount || 0 < from_cpu.amount), - "must unstake a positive amount to buy rex" ); + "must unstake a positive amount to buy rex" ); check_voting_requirement( owner ); { @@ -101,6 +105,10 @@ namespace eosiosystem { add_to_rex_balance( owner, payment, rex_received ); runrex(2); update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ), true ); + // dummy action added so that amount of REX tokens purchased shows up in action trace + dispatch_inline( null_account, "buyresult"_n, + std::vector{ { owner, active_permission } }, + std::make_tuple( rex_received ) ); } /** @@ -117,7 +125,7 @@ namespace eosiosystem { auto bitr = _rexbalance.require_find( from.value, "user must first buyrex" ); check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, - "asset must be a positive amount of (REX, 4)" ); + "asset must be a positive amount of (REX, 4)" ); process_rex_maturities( bitr ); check( rex.amount <= bitr->matured_rex, "insufficient available rex" ); @@ -142,10 +150,16 @@ namespace eosiosystem { _rexorders.modify( oitr, same_payer, [&]( auto& order ) { order.rex_requested.amount += rex.amount; check( order.rex_requested.amount <= bitr->matured_rex, - "insufficient funds for current and scheduled orders"); + "insufficient funds for current and scheduled orders"); }); } } + // dummy action added so that sell order proceeds show up in action trace + if ( current_order.success ) { + dispatch_inline( null_account, "sellresult"_n, + std::vector{ { from, active_permission } }, + std::make_tuple( current_order.proceeds ) ); + } } /** @@ -462,7 +476,6 @@ namespace eosiosystem { rt.total_lent.amount -= itr->total_staked.amount; rt.total_lendable.amount = rt.total_unlent.amount + rt.total_lent.amount; }); - bool delete_loan = false; int64_t delta_stake = 0; if ( itr->payment <= itr->balance && rex_loans_available() ) { @@ -547,11 +560,16 @@ namespace eosiosystem { if ( bitr != _rexbalance.end() ) { // should always be true auto result = fill_rex_order( bitr, oitr->rex_requested ); if ( result.success ) { + const name order_owner = oitr->owner; idx.modify( oitr, same_payer, [&]( auto& order ) { order.proceeds.amount = result.proceeds.amount; order.stake_change.amount = result.stake_change.amount; order.close(); }); + /// send dummy action to show and owner and proceeds of filled sellrex order + dispatch_inline( null_account, "orderresult"_n, + std::vector{ { null_account, active_permission } }, + std::make_tuple( order_owner, result.proceeds ) ); } } oitr = next; @@ -676,8 +694,7 @@ namespace eosiosystem { */ void system_contract::transfer_from_fund( const name& owner, const asset& amount ) { - check( 0 < amount.amount && amount.symbol == core_symbol(), - "must transfer positive amount from REX fund" ); + check( 0 < amount.amount && amount.symbol == core_symbol(), "must transfer positive amount from REX fund" ); auto itr = _rexfunds.require_find( owner.value, "must deposit to REX fund first" ); check( amount <= itr->balance, "insufficient funds"); _rexfunds.modify( itr, same_payer, [&]( auto& fund ) { @@ -693,8 +710,7 @@ namespace eosiosystem { */ void system_contract::transfer_to_fund( const name& owner, const asset& amount ) { - check( 0 < amount.amount && amount.symbol == core_symbol(), - "must transfer positive amount to REX fund" ); + check( 0 < amount.amount && amount.symbol == core_symbol(), "must transfer positive amount to REX fund" ); auto itr = _rexfunds.find( owner.value ); if ( itr == _rexfunds.end() ) { _rexfunds.emplace( owner, [&]( auto& fund ) { @@ -860,7 +876,6 @@ namespace eosiosystem { /// initialize REX pool _rexpool.emplace( _self, [&]( auto& rp ) { rex_received.amount = payment.amount * rex_ratio; - rp.total_lendable = payment; rp.total_lent = asset( 0, core_symbol() ); rp.total_unlent = rp.total_lendable - rp.total_lent; @@ -870,8 +885,7 @@ namespace eosiosystem { }); } else if ( !rex_available() ) { /// should be a rare corner case, REX pool is initialized but empty _rexpool.modify( itr, same_payer, [&]( auto& rp ) { - rex_received.amount = payment.amount * rex_ratio; - + rex_received.amount = payment.amount * rex_ratio; rp.total_lendable.amount = payment.amount; rp.total_lent.amount = 0; rp.total_unlent.amount = rp.total_lendable.amount - rp.total_lent.amount; diff --git a/contracts/eosio.system/src/voting.cpp b/contracts/eosio.system/src/voting.cpp index 153ea9dd7..a84c9f5ad 100755 --- a/contracts/eosio.system/src/voting.cpp +++ b/contracts/eosio.system/src/voting.cpp @@ -206,7 +206,6 @@ namespace eosiosystem { if ( proxy ) { check( producers.size() == 0, "cannot vote for producers and proxy at same time" ); check( voter_name != proxy, "cannot proxy to self" ); - require_recipient( proxy ); } else { check( producers.size() <= 30, "attempt to vote for too many producers" ); for( size_t i = 1; i < producers.size(); ++i ) { diff --git a/tests/eosio.system_tester.hpp b/tests/eosio.system_tester.hpp index 434928d55..0890efe02 100644 --- a/tests/eosio.system_tester.hpp +++ b/tests/eosio.system_tester.hpp @@ -6,6 +6,7 @@ #include #include +#include #include "contracts.hpp" #include "test_symbol.hpp" @@ -312,6 +313,20 @@ class eosio_system_tester : public TESTER { return unstake( acnt, acnt, net, cpu ); } + int64_t bancor_convert( int64_t S, int64_t R, int64_t T ) { return double(R) * T / ( double(S) + T ); }; + + int64_t get_net_limit( account_name a ) { + int64_t ram_bytes = 0, net = 0, cpu = 0; + control->get_resource_limits_manager().get_account_limits( a, ram_bytes, net, cpu ); + return net; + }; + + int64_t get_cpu_limit( account_name a ) { + int64_t ram_bytes = 0, net = 0, cpu = 0; + control->get_resource_limits_manager().get_account_limits( a, ram_bytes, net, cpu ); + return cpu; + }; + action_result deposit( const account_name& owner, const asset& amount ) { return push_action( name(owner), N(deposit), mvo() ("owner", owner) @@ -333,6 +348,22 @@ class eosio_system_tester : public TESTER { ); } + asset get_buyrex_result( const account_name& from, const asset& amount ) { + auto trace = base_tester::push_action( N(eosio), N(buyrex), from, mvo()("from", from)("amount", amount) ); + asset rex_received; + for ( size_t i = 0; i < trace->action_traces.size(); ++i ) { + for ( size_t j = 0; j < trace->action_traces[i].inline_traces.size(); ++j ) { + if ( trace->action_traces[i].inline_traces[j].act.name == N(buyresult) ) { + fc::raw::unpack( trace->action_traces[i].inline_traces[j].act.data.data(), + trace->action_traces[i].inline_traces[j].act.data.size(), + rex_received ); + return rex_received; + } + } + } + return rex_received; + } + action_result unstaketorex( const account_name& owner, const account_name& receiver, const asset& from_net, const asset& from_cpu ) { return push_action( name(owner), N(unstaketorex), mvo() ("owner", owner) @@ -342,6 +373,27 @@ class eosio_system_tester : public TESTER { ); } + asset get_unstaketorex_result( const account_name& owner, const account_name& receiver, const asset& from_net, const asset& from_cpu ) { + auto trace = base_tester::push_action( N(eosio), N(unstaketorex), owner, mvo() + ("owner", owner) + ("receiver", receiver) + ("from_net", from_net) + ("from_cpu", from_cpu) + ); + asset rex_received; + for ( size_t i = 0; i < trace->action_traces.size(); ++i ) { + for ( size_t j = 0; j < trace->action_traces[i].inline_traces.size(); ++j ) { + if ( trace->action_traces[i].inline_traces[j].act.name == N(buyresult) ) { + fc::raw::unpack( trace->action_traces[i].inline_traces[j].act.data.data(), + trace->action_traces[i].inline_traces[j].act.data.size(), + rex_received ); + return rex_received; + } + } + } + return rex_received; + } + action_result sellrex( const account_name& from, const asset& rex ) { return push_action( name(from), N(sellrex), mvo() ("from", from) @@ -349,6 +401,38 @@ class eosio_system_tester : public TESTER { ); } + asset get_sellrex_result( const account_name& from, const asset& rex ) { + auto trace = base_tester::push_action( N(eosio), N(sellrex), from, mvo()("from", from)("rex", rex) ); + asset proceeds; + for ( size_t i = 0; i < trace->action_traces.size(); ++i ) { + for ( size_t j = 0; j < trace->action_traces[i].inline_traces.size(); ++j ) { + if ( trace->action_traces[i].inline_traces[j].act.name == N(sellresult) ) { + fc::raw::unpack( trace->action_traces[i].inline_traces[j].act.data.data(), + trace->action_traces[i].inline_traces[j].act.data.size(), + proceeds ); + return proceeds; + } + } + } + return proceeds; + } + + auto get_rexorder_result( const transaction_trace_ptr& trace ) { + std::vector> output; + for ( size_t i = 0; i < trace->action_traces.size(); ++i ) { + for ( size_t j = 0; j < trace->action_traces[i].inline_traces.size(); ++j ) { + if ( trace->action_traces[i].inline_traces[j].act.name == N(orderresult) ) { + fc::datastream ds( trace->action_traces[i].inline_traces[j].act.data.data(), + trace->action_traces[i].inline_traces[j].act.data.size() ); + account_name owner; fc::raw::unpack( ds, owner ); + asset proceeds; fc::raw::unpack( ds, proceeds ); + output.emplace_back( owner, proceeds ); + } + } + } + return output; + } + action_result cancelrexorder( const account_name& owner ) { return push_action( name(owner), N(cnclrexorder), mvo()("owner", owner) ); } diff --git a/tests/eosio.system_tests.cpp b/tests/eosio.system_tests.cpp index bb237b132..82bdbbe7b 100644 --- a/tests/eosio.system_tests.cpp +++ b/tests/eosio.system_tests.cpp @@ -3398,6 +3398,11 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_rex, eosio_system_tester ) try { account_name alice = accounts[0], bob = accounts[1], carol = accounts[2], emily = accounts[3], frank = accounts[4]; setup_rex_accounts( accounts, init_balance ); + BOOST_REQUIRE_EQUAL( asset::from_string("25000.0000 REX"), get_buyrex_result( alice, core_sym::from_string("2.5000") ) ); + produce_blocks(2); + produce_block(fc::days(5)); + BOOST_REQUIRE_EQUAL( core_sym::from_string("2.5000"), get_sellrex_result( alice, asset::from_string("25000.0000 REX") ) ); + BOOST_REQUIRE_EQUAL( success(), buyrex( alice, core_sym::from_string("13.0000") ) ); BOOST_REQUIRE_EQUAL( core_sym::from_string("13.0000"), get_rex_vote_stake( alice ) ); BOOST_REQUIRE_EQUAL( success(), buyrex( alice, core_sym::from_string("17.0000") ) ); @@ -3448,17 +3453,6 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_rex, eosio_system_tester ) try { BOOST_FIXTURE_TEST_CASE( unstake_buy_rex, eosio_system_tester, * boost::unit_test::tolerance(1e-10) ) try { - auto get_net_limit = [&](account_name a) -> int64_t { - int64_t ram_bytes = 0, net = 0, cpu = 0; - control->get_resource_limits_manager().get_account_limits( a, ram_bytes, net, cpu ); - return net; - }; - auto get_cpu_limit = [&](account_name a) -> int64_t { - int64_t ram_bytes = 0, net = 0, cpu = 0; - control->get_resource_limits_manager().get_account_limits( a, ram_bytes, net, cpu ); - return cpu; - }; - const int64_t ratio = 10000; const asset zero_asset = core_sym::from_string("0.0000"); const asset neg_asset = core_sym::from_string("-0.0001"); @@ -3509,7 +3503,7 @@ BOOST_FIXTURE_TEST_CASE( unstake_buy_rex, eosio_system_tester, * boost::unit_tes BOOST_TEST_REQUIRE( init_prod_info["total_votes"].as_double() == stake2votes( asset( init_voter_info["staked"].as(), symbol{CORE_SYM} ) ) ); produce_block( fc::days(4) ); - BOOST_REQUIRE_EQUAL( success(), unstaketorex( alice, alice, net_stake, cpu_stake ) ); + BOOST_REQUIRE_EQUAL( ratio * tot_stake.get_amount(), get_unstaketorex_result( alice, alice, net_stake, cpu_stake ).get_amount() ); BOOST_REQUIRE_EQUAL( get_cpu_limit( alice ), init_cpu_limit ); BOOST_REQUIRE_EQUAL( get_net_limit( alice ), init_net_limit ); BOOST_REQUIRE_EQUAL( ratio * tot_stake.get_amount(), get_rex_balance( alice ).get_amount() ); @@ -3575,18 +3569,6 @@ BOOST_FIXTURE_TEST_CASE( unstake_buy_rex, eosio_system_tester, * boost::unit_tes BOOST_FIXTURE_TEST_CASE( buy_rent_rex, eosio_system_tester ) try { - auto bancor_convert = [](int64_t S, int64_t R, int64_t T) -> int64_t { return int64_t( double(R * T) / double(S + T) ); }; - auto get_net_limit = [&](account_name a) -> int64_t { - int64_t ram_bytes = 0, net = 0, cpu = 0; - control->get_resource_limits_manager().get_account_limits( a, ram_bytes, net, cpu ); - return net; - }; - auto get_cpu_limit = [&](account_name a) -> int64_t { - int64_t ram_bytes = 0, net = 0, cpu = 0; - control->get_resource_limits_manager().get_account_limits( a, ram_bytes, net, cpu ); - return cpu; - }; - const int64_t ratio = 10000; const asset init_balance = core_sym::from_string("10000.0000"); const asset init_net = core_sym::from_string("70.0000"); @@ -3673,7 +3655,6 @@ BOOST_FIXTURE_TEST_CASE( buy_rent_rex, eosio_system_tester ) try { BOOST_FIXTURE_TEST_CASE( buy_sell_claim_rex, eosio_system_tester ) try { - auto bancor_convert = [](int64_t S, int64_t R, int64_t T) -> int64_t { return int64_t( double(R * T) / double(S + T) ); }; auto within_one = [](int64_t a, int64_t b) -> bool { return std::abs( a - b ) <= 1; }; const asset init_balance = core_sym::from_string("3000000.0000"); @@ -3751,8 +3732,16 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_claim_rex, eosio_system_tester ) try { produce_block( fc::hours(2) ); BOOST_REQUIRE_EQUAL( wasm_assert_msg("rex loans are not currently available"), rentcpu( frank, frank, core_sym::from_string("0.0001") ) ); - - BOOST_REQUIRE_EQUAL( success(), buyrex( frank, core_sym::from_string("0.0001") ) ); + { + auto trace = base_tester::push_action( N(eosio), N(buyrex), frank, + mvo()("from", frank)("amount", core_sym::from_string("0.0001")) ); + auto output = get_rexorder_result( trace ); + BOOST_REQUIRE_EQUAL( output.size(), 2 ); + BOOST_REQUIRE_EQUAL( output[0].first, bob ); + BOOST_REQUIRE_EQUAL( output[0].second, get_rex_order(bob)["proceeds"].as() ); + BOOST_REQUIRE_EQUAL( output[1].first, carol ); + BOOST_REQUIRE_EQUAL( output[1].second, get_rex_order(carol)["proceeds"].as() ); + } BOOST_REQUIRE_EQUAL( true, get_rex_order(alice)["is_open"].as() ); BOOST_REQUIRE_EQUAL( init_alice_rex, get_rex_order(alice)["rex_requested"].as() ); @@ -3773,23 +3762,18 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_claim_rex, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( 0, get_rex_vote_stake( carol ).get_amount() ); BOOST_REQUIRE_EQUAL( init_stake, get_voter_info( carol )["staked"].as() ); + { + auto trace = base_tester::push_action( N(eosio), N(buyrex), frank, + mvo()("from", frank)("amount", core_sym::from_string("0.0001")) ); + auto output = get_rexorder_result( trace ); + BOOST_REQUIRE_EQUAL( output.size(), 0 ); + } + } FC_LOG_AND_RETHROW() BOOST_FIXTURE_TEST_CASE( rex_loans, eosio_system_tester ) try { - auto bancor_convert = [](int64_t S, int64_t R, int64_t T) -> int64_t { return int64_t( double(R * T) / double(S + T) ); }; - auto get_net_limit = [&](account_name a) -> int64_t { - int64_t ram_bytes = 0, net = 0, cpu = 0; - control->get_resource_limits_manager().get_account_limits( a, ram_bytes, net, cpu ); - return net; - }; - auto get_cpu_limit = [&](account_name a) -> int64_t { - int64_t ram_bytes = 0, net = 0, cpu = 0; - control->get_resource_limits_manager().get_account_limits( a, ram_bytes, net, cpu ); - return cpu; - }; - const int64_t ratio = 10000; const asset init_balance = core_sym::from_string("10000.0000"); const std::vector accounts = { N(aliceaccount), N(bobbyaccount), N(carolaccount), N(emilyaccount), N(frankaccount) }; From e44c5a1db9f66bac7c2c7cf49ac9fe6a8928e087 Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Fri, 11 Jan 2019 17:09:15 -0500 Subject: [PATCH 03/23] Remove unnecessary permissions from inline dummy actions --- contracts/eosio.system/src/rex.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index 99e57054c..cc1113b7d 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -60,9 +60,7 @@ namespace eosiosystem { runrex(2); update_rex_account( from, asset( 0, core_symbol() ), delta_rex_stake ); // dummy action added so that amount of REX tokens purchased shows up in action trace - dispatch_inline( null_account, "buyresult"_n, - std::vector{ { from, active_permission } }, - std::make_tuple( rex_received ) ); + dispatch_inline( null_account, "buyresult"_n, { }, std::make_tuple( rex_received ) ); } /** @@ -106,9 +104,7 @@ namespace eosiosystem { runrex(2); update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ), true ); // dummy action added so that amount of REX tokens purchased shows up in action trace - dispatch_inline( null_account, "buyresult"_n, - std::vector{ { owner, active_permission } }, - std::make_tuple( rex_received ) ); + dispatch_inline( null_account, "buyresult"_n, { }, std::make_tuple( rex_received ) ); } /** @@ -156,9 +152,7 @@ namespace eosiosystem { } // dummy action added so that sell order proceeds show up in action trace if ( current_order.success ) { - dispatch_inline( null_account, "sellresult"_n, - std::vector{ { from, active_permission } }, - std::make_tuple( current_order.proceeds ) ); + dispatch_inline( null_account, "sellresult"_n, { }, std::make_tuple( current_order.proceeds ) ); } } @@ -567,9 +561,7 @@ namespace eosiosystem { order.close(); }); /// send dummy action to show and owner and proceeds of filled sellrex order - dispatch_inline( null_account, "orderresult"_n, - std::vector{ { null_account, active_permission } }, - std::make_tuple( order_owner, result.proceeds ) ); + dispatch_inline( null_account, "orderresult"_n, { }, std::make_tuple( order_owner, result.proceeds ) ); } } oitr = next; From 38db481eaea3bfdb70065b174b0e04a35cbe4939 Mon Sep 17 00:00:00 2001 From: Zach <34947245+kj4ezj@users.noreply.github.com> Date: Tue, 15 Jan 2019 11:22:09 -0500 Subject: [PATCH 04/23] Other: Added checkbox to pull request template (#164) --- .github/PULL_REQUEST_TEMPLATE.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 364491293..bff172e40 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,12 +6,18 @@ ## Deployment Changes +- [ ] Deployment Changes + ## API Changes +- [ ] API Changes + ## Documentation Additions +- [ ] Documentation Additions + From 69d7a6c31a12877fed6c7252775bea972767bb70 Mon Sep 17 00:00:00 2001 From: arhag Date: Tue, 15 Jan 2019 14:27:30 -0500 Subject: [PATCH 05/23] add functionality to set explicit resources --- .../include/eosio.system/eosio.system.hpp | 41 ++++- .../eosio.system/src/delegate_bandwidth.cpp | 42 ++++- contracts/eosio.system/src/eosio.system.cpp | 148 +++++++++++++++++- contracts/eosio.system/src/rex.cpp | 61 +++++--- 4 files changed, 259 insertions(+), 33 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index ff7758a2c..2cd6e22fc 100755 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -13,6 +13,8 @@ #include #include +#include +#include #ifdef CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX #undef CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX @@ -37,6 +39,25 @@ namespace eosiosystem { using eosio::datastream; using eosio::check; + template + static inline auto has_field( F flags, E field ) + -> std::enable_if_t< std::is_integral_v && std::is_unsigned_v && + std::is_enum_v && std::is_same_v< F, std::underlying_type_t >, bool> + { + return ( (flags & static_cast(field)) != 0 ); + } + + template + static inline auto set_field( F flags, E field, bool value = true ) + -> std::enable_if_t< std::is_integral_v && std::is_unsigned_v && + std::is_enum_v && std::is_same_v< F, std::underlying_type_t >, F > + { + if( value ) + return ( flags | static_cast(field) ); + else + return ( flags & ~static_cast(field) ); + } + struct [[eosio::table, eosio::contract("eosio.system")]] name_bid { name newname; name high_bidder; @@ -162,14 +183,20 @@ namespace eosiosystem { bool is_proxy = 0; /// whether the voter is a proxy for others - uint32_t reserved1 = 0; + uint32_t flags1 = 0; uint32_t reserved2 = 0; eosio::asset reserved3; uint64_t primary_key()const { return owner.value; } + enum class flags1_fields : uint32_t { + ram_managed = 1, + net_managed = 2, + cpu_managed = 4 + }; + // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE( voter_info, (owner)(proxy)(producers)(staked)(last_vote_weight)(proxied_vote_weight)(is_proxy)(reserved1)(reserved2)(reserved3) ) + EOSLIB_SERIALIZE( voter_info, (owner)(proxy)(producers)(staked)(last_vote_weight)(proxied_vote_weight)(is_proxy)(flags1)(reserved2)(reserved3) ) }; typedef eosio::multi_index< "voters"_n, voter_info > voters_table; @@ -322,6 +349,16 @@ namespace eosiosystem { [[eosio::action]] void setalimits( name account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight ); + + [[eosio::action]] + void setacctram( name account, std::optional ram_bytes ); + + [[eosio::action]] + void setacctnet( name account, std::optional net_weight ); + + [[eosio::action]] + void setacctcpu( name account, std::optional cpu_weight ); + // functions defined in delegate_bandwidth.cpp /** diff --git a/contracts/eosio.system/src/delegate_bandwidth.cpp b/contracts/eosio.system/src/delegate_bandwidth.cpp index 1b574da3a..a33bb63c5 100755 --- a/contracts/eosio.system/src/delegate_bandwidth.cpp +++ b/contracts/eosio.system/src/delegate_bandwidth.cpp @@ -163,7 +163,13 @@ namespace eosiosystem { res.ram_bytes += bytes_out; }); } - set_resource_limits( res_itr->owner.value, res_itr->ram_bytes + ram_gift_bytes, res_itr->net_weight.amount, res_itr->cpu_weight.amount ); + + auto voter_itr = _voters.find( res_itr->owner.value ); + if( voter_itr == _voters.end() || !has_field( voter_itr->flags1, voter_info::flags1_fields::ram_managed ) ) { + int64_t ram_bytes, net, cpu; + get_resource_limits( res_itr->owner.value, &ram_bytes, &net, &cpu ); + set_resource_limits( res_itr->owner.value, res_itr->ram_bytes + ram_gift_bytes, net, cpu ); + } } /** @@ -201,7 +207,13 @@ namespace eosiosystem { userres.modify( res_itr, account, [&]( auto& res ) { res.ram_bytes -= bytes; }); - set_resource_limits( res_itr->owner.value, res_itr->ram_bytes + ram_gift_bytes, res_itr->net_weight.amount, res_itr->cpu_weight.amount ); + + auto voter_itr = _voters.find( res_itr->owner.value ); + if( voter_itr == _voters.end() || !has_field( voter_itr->flags1, voter_info::flags1_fields::ram_managed ) ) { + int64_t ram_bytes, net, cpu; + get_resource_limits( res_itr->owner.value, &ram_bytes, &net, &cpu ); + set_resource_limits( res_itr->owner.value, res_itr->ram_bytes + ram_gift_bytes, net, cpu ); + } INLINE_ACTION_SENDER(eosio::token, transfer)( token_account, { {ram_account, active_permission}, {account, active_permission} }, @@ -285,10 +297,28 @@ namespace eosiosystem { check( 0 <= tot_itr->net_weight.amount, "insufficient staked total net bandwidth" ); check( 0 <= tot_itr->cpu_weight.amount, "insufficient staked total cpu bandwidth" ); - int64_t ram_bytes, net, cpu; - get_resource_limits( receiver.value, &ram_bytes, &net, &cpu ); - - set_resource_limits( receiver.value, std::max( tot_itr->ram_bytes + ram_gift_bytes, ram_bytes ), tot_itr->net_weight.amount, tot_itr->cpu_weight.amount ); + { + bool ram_managed = false; + bool net_managed = false; + bool cpu_managed = false; + + auto voter_itr = _voters.find( receiver.value ); + if( voter_itr != _voters.end() ) { + ram_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::ram_managed ); + net_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::net_managed ); + cpu_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::cpu_managed ); + } + + if( !(net_managed && cpu_managed) ) { + int64_t ram_bytes, net, cpu; + get_resource_limits( receiver.value, &ram_bytes, &net, &cpu ); + + set_resource_limits( receiver.value, + ram_managed ? ram_bytes : std::max( tot_itr->ram_bytes + ram_gift_bytes, ram_bytes ), + net_managed ? net : tot_itr->net_weight.amount, + cpu_managed ? cpu : tot_itr->cpu_weight.amount ); + } + } if ( tot_itr->is_empty() ) { totals_tbl.erase( tot_itr ); diff --git a/contracts/eosio.system/src/eosio.system.cpp b/contracts/eosio.system/src/eosio.system.cpp index 3e8374c3b..608d818f8 100755 --- a/contracts/eosio.system/src/eosio.system.cpp +++ b/contracts/eosio.system/src/eosio.system.cpp @@ -128,12 +128,155 @@ namespace eosiosystem { void system_contract::setalimits( name account, int64_t ram, int64_t net, int64_t cpu ) { require_auth( _self ); + user_resources_table userres( _self, account.value ); auto ritr = userres.find( account.value ); check( ritr == userres.end(), "only supports unlimited accounts" ); + + auto vitr = _voters.find( account.value ); + if( vitr != _voters.end() ) { + bool ram_managed = has_field( vitr->flags1, voter_info::flags1_fields::ram_managed ); + bool net_managed = has_field( vitr->flags1, voter_info::flags1_fields::net_managed ); + bool cpu_managed = has_field( vitr->flags1, voter_info::flags1_fields::cpu_managed ); + check( !(ram_managed || net_managed || cpu_managed), "cannot use setalimits on an account with managed resources" ); + } + set_resource_limits( account.value, ram, net, cpu ); } + void system_contract::setacctram( name account, std::optional ram_bytes ) { + require_auth( _self ); + + int64_t current_ram, current_net, current_cpu; + get_resource_limits( account.value, ¤t_ram, ¤t_net, ¤t_cpu ); + + int64_t ram = 0; + + if( !ram_bytes ) { + auto vitr = _voters.find( account.value ); + check( vitr != _voters.end() && has_field( vitr->flags1, voter_info::flags1_fields::ram_managed ), + "RAM of account is already unmanaged" ); + + user_resources_table userres( _self, account.value ); + auto ritr = userres.find( account.value ); + + ram = ram_gift_bytes; + if( ritr != userres.end() ) { + ram += ritr->ram_bytes; + } + + _voters.modify( vitr, same_payer, [&]( auto& v ) { + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::ram_managed, false ); + }); + } else { + check( *ram_bytes >= 0, "not allowed to set RAM limit to unlimited" ); + + auto vitr = _voters.find( account.value ); + if ( vitr != _voters.end() ) { + _voters.modify( vitr, same_payer, [&]( auto& v ) { + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::ram_managed, true ); + }); + } else { + _voters.emplace( account, [&]( auto& v ) { + v.owner = account; + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::ram_managed, true ); + }); + } + + ram = *ram_bytes; + } + + set_resource_limits( account.value, ram, current_net, current_cpu ); + } + + void system_contract::setacctnet( name account, std::optional net_weight ) { + require_auth( _self ); + + int64_t current_ram, current_net, current_cpu; + get_resource_limits( account.value, ¤t_ram, ¤t_net, ¤t_cpu ); + + int64_t net = 0; + + if( !net_weight ) { + auto vitr = _voters.find( account.value ); + check( vitr != _voters.end() && has_field( vitr->flags1, voter_info::flags1_fields::net_managed ), + "Network bandwidth of account is already unmanaged" ); + + user_resources_table userres( _self, account.value ); + auto ritr = userres.find( account.value ); + + if( ritr != userres.end() ) { + net = ritr->net_weight.amount; + } + + _voters.modify( vitr, same_payer, [&]( auto& v ) { + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::net_managed, false ); + }); + } else { + check( *net_weight >= -1, "invalid value for net_weight" ); + + auto vitr = _voters.find( account.value ); + if ( vitr != _voters.end() ) { + _voters.modify( vitr, same_payer, [&]( auto& v ) { + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::net_managed, true ); + }); + } else { + _voters.emplace( account, [&]( auto& v ) { + v.owner = account; + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::net_managed, true ); + }); + } + + net = *net_weight; + } + + set_resource_limits( account.value, current_ram, net, current_cpu ); + } + + void system_contract::setacctcpu( name account, std::optional cpu_weight ) { + require_auth( _self ); + + int64_t current_ram, current_net, current_cpu; + get_resource_limits( account.value, ¤t_ram, ¤t_net, ¤t_cpu ); + + int64_t cpu = 0; + + if( !cpu_weight ) { + auto vitr = _voters.find( account.value ); + check( vitr != _voters.end() && has_field( vitr->flags1, voter_info::flags1_fields::cpu_managed ), + "CPU bandwidth of account is already unmanaged" ); + + user_resources_table userres( _self, account.value ); + auto ritr = userres.find( account.value ); + + if( ritr != userres.end() ) { + cpu = ritr->cpu_weight.amount; + } + + _voters.modify( vitr, same_payer, [&]( auto& v ) { + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::cpu_managed, false ); + }); + } else { + check( *cpu_weight >= -1, "invalid value for cpu_weight" ); + + auto vitr = _voters.find( account.value ); + if ( vitr != _voters.end() ) { + _voters.modify( vitr, same_payer, [&]( auto& v ) { + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::cpu_managed, true ); + }); + } else { + _voters.emplace( account, [&]( auto& v ) { + v.owner = account; + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::cpu_managed, true ); + }); + } + + cpu = *cpu_weight; + } + + set_resource_limits( account.value, current_ram, current_net, cpu ); + } + void system_contract::rmvproducer( name producer ) { require_auth( _self ); auto prod = _producers.find( producer.value ); @@ -308,7 +451,7 @@ namespace eosiosystem { m.quote.balance.amount = system_token_supply.amount / 1000; m.quote.balance.symbol = core; }); - + INLINE_ACTION_SENDER(eosio::token, open)( token_account, { _self, active_permission }, { rex_account, core, _self } ); } @@ -320,7 +463,8 @@ EOSIO_DISPATCH( eosiosystem::system_contract, // native.hpp (newaccount definition is actually in eosio.system.cpp) (newaccount)(updateauth)(deleteauth)(linkauth)(unlinkauth)(canceldelay)(onerror)(setabi) // eosio.system.cpp - (init)(setram)(setramrate)(setparams)(setpriv)(setalimits)(rmvproducer)(updtrevision)(bidname)(bidrefund) + (init)(setram)(setramrate)(setparams)(setpriv)(setalimits)(setacctram)(setacctnet)(setacctcpu) + (rmvproducer)(updtrevision)(bidname)(bidrefund) // rex.cpp (deposit)(withdraw)(buyrex)(unstaketorex)(sellrex)(cnclrexorder)(rentcpu)(rentnet)(fundcpuloan)(fundnetloan) (defcpuloan)(defnetloan)(updaterex)(consolidate)(rexexec)(closerex) diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index db4360b0f..ba98a09cb 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -410,33 +410,48 @@ namespace eosiosystem { return; } + user_resources_table totals_tbl( _self, receiver.value ); + auto tot_itr = totals_tbl.find( receiver.value ); + if ( tot_itr == totals_tbl.end() ) { + check( 0 <= delta_net && 0 <= delta_cpu, "logic error, should not occur"); + tot_itr = totals_tbl.emplace( from, [&]( auto& tot ) { + tot.owner = receiver; + tot.net_weight = asset( delta_net, core_symbol() ); + tot.cpu_weight = asset( delta_cpu, core_symbol() ); + }); + } else { + totals_tbl.modify( tot_itr, same_payer, [&]( auto& tot ) { + tot.net_weight.amount += delta_net; + tot.cpu_weight.amount += delta_cpu; + }); + } + check( 0 <= tot_itr->net_weight.amount, "insufficient staked total net bandwidth" ); + check( 0 <= tot_itr->cpu_weight.amount, "insufficient staked total cpu bandwidth" ); + + if ( tot_itr->is_empty() ) { + totals_tbl.erase( tot_itr ); + } + { - user_resources_table totals_tbl( _self, receiver.value ); - auto tot_itr = totals_tbl.find( receiver.value ); - if ( tot_itr == totals_tbl.end() ) { - check( 0 <= delta_net && 0 <= delta_cpu, "logic error, should not occur"); - tot_itr = totals_tbl.emplace( from, [&]( auto& tot ) { - tot.owner = receiver; - tot.net_weight = asset( delta_net, core_symbol() ); - tot.cpu_weight = asset( delta_cpu, core_symbol() ); - }); - } else { - totals_tbl.modify( tot_itr, same_payer, [&]( auto& tot ) { - tot.net_weight.amount += delta_net; - tot.cpu_weight.amount += delta_cpu; - }); + bool net_managed = false; + bool cpu_managed = false; + + auto voter_itr = _voters.find( receiver.value ); + if( voter_itr != _voters.end() ) { + net_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::net_managed ); + cpu_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::cpu_managed ); } - check( 0 <= tot_itr->net_weight.amount, "insufficient staked total net bandwidth" ); - check( 0 <= tot_itr->cpu_weight.amount, "insufficient staked total cpu bandwidth" ); - - if ( tot_itr->is_empty() ) { - totals_tbl.erase( tot_itr ); + + if( !(net_managed && cpu_managed) ) { + int64_t ram_bytes = 0, net = 0, cpu = 0; + get_resource_limits( receiver.value, &ram_bytes, &net, &cpu ); + + set_resource_limits( receiver.value, + ram_bytes, + net_managed ? net : tot_itr->net_weight.amount, + cpu_managed ? cpu : tot_itr->cpu_weight.amount ); } } - - int64_t ram_bytes = 0, net = 0, cpu = 0; - get_resource_limits( receiver.value, &ram_bytes, &net, &cpu ); - set_resource_limits( receiver.value, ram_bytes, net + delta_net, cpu + delta_cpu ); } void system_contract::check_voting_requirement( const name& owner, const char* error_msg )const From 8eef0fc612e0b6f20872b09bc2f29a17a271105b Mon Sep 17 00:00:00 2001 From: arhag Date: Tue, 15 Jan 2019 14:28:11 -0500 Subject: [PATCH 06/23] unit tests for new actions to set explicit resources --- tests/eosio.system_tests.cpp | 218 ++++++++++++++++++++++++++++------- 1 file changed, 179 insertions(+), 39 deletions(-) diff --git a/tests/eosio.system_tests.cpp b/tests/eosio.system_tests.cpp index bb237b132..4740cebc6 100644 --- a/tests/eosio.system_tests.cpp +++ b/tests/eosio.system_tests.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -3392,7 +3393,7 @@ BOOST_FIXTURE_TEST_CASE( ram_gift, eosio_system_tester ) try { BOOST_FIXTURE_TEST_CASE( buy_sell_rex, eosio_system_tester ) try { const int64_t ratio = 10000; - const asset init_rent = core_sym::from_string("100000.0000"); + const asset init_rent = core_sym::from_string("100000.0000"); const asset init_balance = core_sym::from_string("1000.0000"); const std::vector accounts = { N(aliceaccount), N(bobbyaccount), N(carolaccount), N(emilyaccount), N(frankaccount) }; account_name alice = accounts[0], bob = accounts[1], carol = accounts[2], emily = accounts[3], frank = accounts[4]; @@ -3428,7 +3429,7 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_rex, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( wasm_assert_msg("asset must be a positive amount of (REX, 4)"), sellrex( bob, asset::from_string("-75.0030 REX") ) ); BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient available rex"), sellrex( bob, asset::from_string("750000.0030 REX") ) ); - + auto init_total_rex = rex_pool["total_rex"].as().get_amount(); auto init_total_lendable = rex_pool["total_lendable"].as().get_amount(); BOOST_REQUIRE_EQUAL( success(), sellrex( bob, asset::from_string("550000.6800 REX") ) ); @@ -3523,7 +3524,7 @@ BOOST_FIXTURE_TEST_CASE( unstake_buy_rex, eosio_system_tester, * boost::unit_tes stake2votes( asset( current_voter_info["staked"].as(), symbol{CORE_SYM} ) ) ); BOOST_TEST_REQUIRE( init_prod_info["total_votes"].as_double() < current_prod_info["total_votes"].as_double() ); } - + { const asset net_stake = core_sym::from_string("200.5000"); const asset cpu_stake = core_sym::from_string("120.0000"); @@ -3607,7 +3608,7 @@ BOOST_FIXTURE_TEST_CASE( buy_rent_rex, eosio_system_tester ) try { const asset init_tot_unlent = rex_pool["total_unlent"].as(); const asset init_tot_lendable = rex_pool["total_lendable"].as(); const asset init_tot_rent = rex_pool["total_rent"].as(); - const int64_t init_stake = get_voter_info(alice)["staked"].as(); + const int64_t init_stake = get_voter_info(alice)["staked"].as(); BOOST_REQUIRE_EQUAL( core_sym::from_string("0.0000"), rex_pool["total_lent"].as() ); BOOST_REQUIRE_EQUAL( ratio * init_tot_lendable.get_amount(), rex_pool["total_rex"].as().get_amount() ); BOOST_REQUIRE_EQUAL( rex_pool["total_rex"].as(), get_rex_balance(alice) ); @@ -3625,7 +3626,7 @@ BOOST_FIXTURE_TEST_CASE( buy_rent_rex, eosio_system_tester ) try { rex_pool["total_lent"].as().get_amount() ); BOOST_REQUIRE_EQUAL( rex_pool["total_lent"].as() + rex_pool["total_unlent"].as(), rex_pool["total_lendable"].as() ); - + // test that carol's resource limits have been updated properly BOOST_REQUIRE_EQUAL( expected_total_lent, get_cpu_limit( carol ) - init_cpu_limit ); BOOST_REQUIRE_EQUAL( 0, get_net_limit( carol ) - init_net_limit ); @@ -3636,7 +3637,7 @@ BOOST_FIXTURE_TEST_CASE( buy_rent_rex, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( success(), sellrex( alice, get_rex_balance(alice) ) ); BOOST_REQUIRE_EQUAL( success(), cancelrexorder( alice ) ); BOOST_REQUIRE_EQUAL( rex_pool["total_rex"].as(), get_rex_balance(alice) ); - + produce_block( fc::days(20) ); BOOST_REQUIRE_EQUAL( success(), sellrex( alice, get_rex_balance(alice) ) ); BOOST_REQUIRE_EQUAL( success(), cancelrexorder( alice ) ); @@ -3648,7 +3649,7 @@ BOOST_FIXTURE_TEST_CASE( buy_rent_rex, eosio_system_tester ) try { // test that carol's resource limits have been updated properly when loan expires BOOST_REQUIRE_EQUAL( init_cpu_limit, get_cpu_limit( carol ) ); BOOST_REQUIRE_EQUAL( init_net_limit, get_net_limit( carol ) ); - + rex_pool = get_rex_pool(); BOOST_REQUIRE_EQUAL( 0, rex_pool["total_lendable"].as().get_amount() ); BOOST_REQUIRE_EQUAL( 0, rex_pool["total_unlent"].as().get_amount() ); @@ -3688,12 +3689,12 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_claim_rex, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( success(), buyrex( alice, purchase1) ); BOOST_REQUIRE_EQUAL( success(), buyrex( bob, purchase2) ); BOOST_REQUIRE_EQUAL( success(), buyrex( carol, purchase3) ); - + BOOST_REQUIRE_EQUAL( init_balance - purchase1, get_rex_fund(alice) ); BOOST_REQUIRE_EQUAL( purchase1.get_amount(), get_voter_info(alice)["staked"].as() - init_stake ); BOOST_REQUIRE_EQUAL( init_balance - purchase2, get_rex_fund(bob) ); BOOST_REQUIRE_EQUAL( init_balance - purchase3, get_rex_fund(carol) ); - + auto init_alice_rex = get_rex_balance(alice); auto init_bob_rex = get_rex_balance(bob); auto init_carol_rex = get_rex_balance(carol); @@ -3706,9 +3707,9 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_claim_rex, eosio_system_tester ) try { const auto init_rex_pool = get_rex_pool(); const int64_t init_alice_rex_stake = ( eosio::chain::uint128_t(init_alice_rex.get_amount()) * init_rex_pool["total_lendable"].as().get_amount() ) / init_rex_pool["total_rex"].as().get_amount(); - + produce_block( fc::days(5) ); - + BOOST_REQUIRE_EQUAL( success(), sellrex( alice, asset( get_rex_balance(alice).get_amount() / 4, symbol(SY(4,REX)) ) ) ); BOOST_TEST_REQUIRE( within_one( 3 * init_alice_rex.get_amount() / 4, get_rex_balance(alice).get_amount() ) ); @@ -3725,7 +3726,7 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_claim_rex, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( init_bob_rex, get_rex_balance(bob) ); BOOST_REQUIRE_EQUAL( init_carol_rex, get_rex_balance(carol) ); BOOST_REQUIRE_EQUAL( init_alice_rex, get_rex_balance(alice) ); - + // now bob's, carol's and alice's sellrex orders have been queued BOOST_REQUIRE_EQUAL( true, get_rex_order(alice)["is_open"].as() ); BOOST_REQUIRE_EQUAL( init_alice_rex, get_rex_order(alice)["rex_requested"].as() ); @@ -3744,7 +3745,7 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_claim_rex, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( true, get_rex_order(bob)["is_open"].as() ); BOOST_REQUIRE_EQUAL( true, get_rex_order(carol)["is_open"].as() ); - // wait for 2 more hours, by now frank's loan has expired and there is enough balance in + // wait for 2 more hours, by now frank's loan has expired and there is enough balance in // total_unlent to close some sellrex orders. only two are processed, bob's and carol's // alices's order is still open // an action is needed to trigger queue processing @@ -3765,7 +3766,7 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_claim_rex, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( false, get_rex_order(carol)["is_open"].as() ); BOOST_REQUIRE_EQUAL( init_carol_rex, get_rex_order(carol)["rex_requested"].as() ); BOOST_REQUIRE ( 0 < get_rex_order(carol)["proceeds"].as().get_amount() ); - + BOOST_REQUIRE_EQUAL( success(), updaterex( bob ) ); BOOST_REQUIRE_EQUAL( 0, get_rex_vote_stake( bob ).get_amount() ); BOOST_REQUIRE_EQUAL( init_stake, get_voter_info( bob )["staked"].as() ); @@ -3797,7 +3798,7 @@ BOOST_FIXTURE_TEST_CASE( rex_loans, eosio_system_tester ) try { setup_rex_accounts( accounts, init_balance ); BOOST_REQUIRE_EQUAL( success(), buyrex( alice, core_sym::from_string("500.0000") ) ); - + auto rex_pool = get_rex_pool(); const asset payment = core_sym::from_string("30.0000"); const asset zero_asset = core_sym::from_string("0.0000"); @@ -3807,7 +3808,7 @@ BOOST_FIXTURE_TEST_CASE( rex_loans, eosio_system_tester ) try { int64_t expected_stake = bancor_convert( rex_pool["total_rent"].as().get_amount(), rex_pool["total_unlent"].as().get_amount(), payment.get_amount() ); - const int64_t init_stake = get_cpu_limit( frank ); + const int64_t init_stake = get_cpu_limit( frank ); BOOST_REQUIRE_EQUAL( wasm_assert_msg("must use core token"), rentcpu( frank, bob, asset::from_string("10.0000 RND") ) ); @@ -3868,7 +3869,7 @@ BOOST_FIXTURE_TEST_CASE( rex_loans, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( fundcpuloan( frank, 1, amount ), success() ); BOOST_REQUIRE_EQUAL( old_loan_balance + amount, get_cpu_loan(1)["balance"].as() ); cur_frank_balance = get_rex_fund( frank ); - BOOST_REQUIRE_EQUAL( old_frank_balance - amount, cur_frank_balance ); + BOOST_REQUIRE_EQUAL( old_frank_balance - amount, cur_frank_balance ); } // wait for 30 days, frank's loan will be renewed at the current price @@ -3878,14 +3879,14 @@ BOOST_FIXTURE_TEST_CASE( rex_loans, eosio_system_tester ) try { int64_t unlent_tokens = bancor_convert( rex_pool["total_unlent"].as().get_amount(), rex_pool["total_rent"].as().get_amount(), expected_stake ); - - expected_stake = bancor_convert( rex_pool["total_rent"].as().get_amount() - unlent_tokens, + + expected_stake = bancor_convert( rex_pool["total_rent"].as().get_amount() - unlent_tokens, rex_pool["total_unlent"].as().get_amount() + expected_stake, payment.get_amount() ); } - + BOOST_REQUIRE_EQUAL( success(), sellrex( alice, asset::from_string("1.0000 REX") ) ); - + loan_info = get_cpu_loan(1); BOOST_REQUIRE_EQUAL( payment, loan_info["payment"].as() ); BOOST_REQUIRE_EQUAL( fund - payment, loan_info["balance"].as() ); @@ -3935,7 +3936,7 @@ BOOST_FIXTURE_TEST_CASE( ramfee_namebid_to_rex, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( success(), buyram( bob, carol, core_sym::from_string("70.0000") ) ); BOOST_REQUIRE_EQUAL( cur_ramfee_balance, get_balance( N(eosio.ramfee) ) ); BOOST_REQUIRE_EQUAL( get_balance( N(eosio.rex) ), cur_rex_balance + core_sym::from_string("0.3500") ); - + cur_rex_balance = get_balance( N(eosio.rex) ); auto cur_rex_pool = get_rex_pool(); @@ -3944,7 +3945,7 @@ BOOST_FIXTURE_TEST_CASE( ramfee_namebid_to_rex, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( cur_rex_balance, cur_rex_pool["total_lendable"].as() ); BOOST_REQUIRE_EQUAL( 0, cur_rex_pool["namebid_proceeds"].as().get_amount() ); - // required for closing namebids + // required for closing namebids cross_15_percent_threshold(); produce_block( fc::days(14) ); @@ -3986,12 +3987,12 @@ BOOST_FIXTURE_TEST_CASE( rex_maturity, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( success(), buyrex( alice, core_sym::from_string("18.5000") ) ); produce_block( fc::hours(25) ); BOOST_REQUIRE_EQUAL( success(), buyrex( alice, core_sym::from_string("25.0000") ) ); - + auto rex_balance = get_rex_balance_obj( alice ); BOOST_REQUIRE_EQUAL( 550000 * rex_ratio, rex_balance["rex_balance"].as().get_amount() ); BOOST_REQUIRE_EQUAL( 0, rex_balance["matured_rex"].as() ); BOOST_REQUIRE_EQUAL( 2, rex_balance["rex_maturities"].get_array().size() ); - + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient available rex"), sellrex( alice, asset::from_string("115000.0000 REX") ) ); produce_block( fc::hours( 3*24 + 20) ); @@ -4017,7 +4018,7 @@ BOOST_FIXTURE_TEST_CASE( rex_maturity, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( 0, rex_balance["matured_rex"].as() ); BOOST_REQUIRE_EQUAL( 0, rex_balance["rex_maturities"].get_array().size() ); } - + { const asset payment1 = core_sym::from_string("14.8000"); const asset payment2 = core_sym::from_string("15.2000"); @@ -4029,22 +4030,22 @@ BOOST_FIXTURE_TEST_CASE( rex_maturity, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( success(), buyrex( bob, payment2 ) ); produce_block( fc::hours(24) ); } - + auto rex_balance = get_rex_balance_obj( bob ); BOOST_REQUIRE_EQUAL( 8 * rex_bucket.get_amount(), rex_balance["rex_balance"].as().get_amount() ); BOOST_REQUIRE_EQUAL( 5, rex_balance["rex_maturities"].get_array().size() ); BOOST_REQUIRE_EQUAL( 3 * rex_bucket.get_amount(), rex_balance["matured_rex"].as() ); - + BOOST_REQUIRE_EQUAL( success(), updaterex( bob ) ); rex_balance = get_rex_balance_obj( bob ); BOOST_REQUIRE_EQUAL( 4, rex_balance["rex_maturities"].get_array().size() ); BOOST_REQUIRE_EQUAL( 4 * rex_bucket.get_amount(), rex_balance["matured_rex"].as() ); - + produce_block( fc::hours(2) ); BOOST_REQUIRE_EQUAL( success(), updaterex( bob ) ); rex_balance = get_rex_balance_obj( bob ); BOOST_REQUIRE_EQUAL( 4, rex_balance["rex_maturities"].get_array().size() ); - + produce_block( fc::hours(1) ); BOOST_REQUIRE_EQUAL( success(), sellrex( bob, asset( 3 * rex_bucket.get_amount(), rex_sym ) ) ); rex_balance = get_rex_balance_obj( bob ); @@ -4064,12 +4065,12 @@ BOOST_FIXTURE_TEST_CASE( rex_maturity, eosio_system_tester ) try { rex_balance = get_rex_balance_obj( bob ); BOOST_REQUIRE_EQUAL( 3, rex_balance["rex_maturities"].get_array().size() ); BOOST_REQUIRE_EQUAL( rex_bucket.get_amount(), rex_balance["matured_rex"].as() ); - + BOOST_REQUIRE_EQUAL( success(), consolidate( bob ) ); rex_balance = get_rex_balance_obj( bob ); BOOST_REQUIRE_EQUAL( 1, rex_balance["rex_maturities"].get_array().size() ); BOOST_REQUIRE_EQUAL( 0, rex_balance["matured_rex"].as() ); - + produce_block( fc::days(3) ); BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient available rex"), sellrex( bob, asset( 4 * rex_bucket.get_amount(), rex_sym ) ) ); @@ -4093,14 +4094,14 @@ BOOST_FIXTURE_TEST_CASE( rex_maturity, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( success(), buyrex( bob, payment2 ) ); produce_block( fc::days(2) ); BOOST_REQUIRE_EQUAL( success(), updaterex( bob ) ); - + auto rex_balance = get_rex_balance_obj( bob ); BOOST_REQUIRE_EQUAL( tot_rex, rex_balance["rex_balance"].as() ); BOOST_REQUIRE_EQUAL( rex_bucket1.get_amount(), rex_balance["matured_rex"].as() ); BOOST_REQUIRE_EQUAL( success(), rentcpu( alice, alice, core_sym::from_string("800000.0000") ) ); BOOST_REQUIRE_EQUAL( success(), sellrex( bob, asset( rex_bucket1.get_amount() - 20, rex_sym ) ) ); rex_balance = get_rex_balance_obj( bob ); - BOOST_REQUIRE_EQUAL( rex_bucket1.get_amount(), get_rex_order( bob )["rex_requested"].as().get_amount() + 20 ); + BOOST_REQUIRE_EQUAL( rex_bucket1.get_amount(), get_rex_order( bob )["rex_requested"].as().get_amount() + 20 ); BOOST_REQUIRE_EQUAL( tot_rex, rex_balance["rex_balance"].as() ); BOOST_REQUIRE_EQUAL( rex_bucket1.get_amount(), rex_balance["matured_rex"].as() ); BOOST_REQUIRE_EQUAL( success(), consolidate( bob ) ); @@ -4154,7 +4155,7 @@ BOOST_FIXTURE_TEST_CASE( update_rex, eosio_system_tester, * boost::unit_test::to BOOST_REQUIRE_EQUAL( success(), vote( alice, std::vector(producer_names.begin(), producer_names.begin() + 21) ) ); - BOOST_TEST_REQUIRE( stake2votes( asset( get_voter_info( alice )["staked"].as(), symbol{CORE_SYM} ) ) + BOOST_TEST_REQUIRE( stake2votes( asset( get_voter_info( alice )["staked"].as(), symbol{CORE_SYM} ) ) == get_producer_info(producer_names[0])["total_votes"].as() ); BOOST_TEST_REQUIRE( stake2votes( asset( get_voter_info( alice )["staked"].as(), symbol{CORE_SYM} ) ) == get_producer_info(producer_names[20])["total_votes"].as() ); @@ -4203,7 +4204,7 @@ BOOST_FIXTURE_TEST_CASE( deposit_rex_fund, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( wasm_assert_msg("must deposit to REX fund first"), withdraw( alice, core_sym::from_string("0.0001") ) ); BOOST_REQUIRE_EQUAL( wasm_assert_msg("overdrawn balance"), deposit( alice, init_balance + init_balance ) ); BOOST_REQUIRE_EQUAL( wasm_assert_msg("must deposit core token"), deposit( alice, asset::from_string("1.0000 RNDM") ) ); - + asset deposit_quant( init_balance.get_amount() / 5, init_balance.get_symbol() ); BOOST_REQUIRE_EQUAL( success(), deposit( alice, deposit_quant ) ); BOOST_REQUIRE_EQUAL( get_balance( alice ), init_balance - deposit_quant ); @@ -4233,7 +4234,7 @@ BOOST_FIXTURE_TEST_CASE( rex_lower_bound, eosio_system_tester ) try { const asset payment = core_sym::from_string("50.0000"); BOOST_REQUIRE_EQUAL( success(), buyrex( alice, payment ) ); BOOST_REQUIRE_EQUAL( success(), rentcpu( bob, bob, payment ) ); - + const auto rex_pool = get_rex_pool(); const int64_t tot_rex = rex_pool["total_rex"].as().get_amount(); const int64_t tot_unlent = rex_pool["total_unlent"].as().get_amount(); @@ -4284,7 +4285,7 @@ BOOST_FIXTURE_TEST_CASE( close_rex, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( success(), deposit( bob, init_balance ) ); BOOST_REQUIRE_EQUAL( success(), buyrex( bob, init_balance ) ); - + BOOST_REQUIRE_EQUAL( success(), rentcpu( carol, emily, init_balance ) ); produce_block( fc::days(20) ); @@ -4296,7 +4297,7 @@ BOOST_FIXTURE_TEST_CASE( close_rex, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( success(), closerex( carol ) ); BOOST_REQUIRE_EQUAL( true, get_rex_balance_obj( carol ).is_null() ); BOOST_REQUIRE_EQUAL( true, get_rex_fund_obj( carol ).is_null() ); - + BOOST_REQUIRE_EQUAL( success(), rentnet( emily, emily, init_balance ) ); BOOST_REQUIRE_EQUAL( success(), closerex( emily ) ); @@ -4377,4 +4378,143 @@ BOOST_FIXTURE_TEST_CASE( setabi, eosio_system_tester ) try { } FC_LOG_AND_RETHROW() +BOOST_FIXTURE_TEST_CASE( change_limited_account_back_to_unlimited, eosio_system_tester ) try { + BOOST_REQUIRE( get_total_stake( "eosio" ).is_null() ); + + transfer( N(eosio), N(alice1111111), core_sym::from_string("1.0000") ); + + auto error_msg = stake( N(alice1111111), N(eosio), core_sym::from_string("0.0000"), core_sym::from_string("1.0000") ); + auto semicolon_pos = error_msg.find(';'); + + BOOST_REQUIRE_EQUAL( error("account eosio has insufficient ram"), + error_msg.substr(0, semicolon_pos) ); + + int64_t ram_bytes_needed = 0; + { + std::istringstream s( error_msg ); + s.seekg( semicolon_pos + 7, std::ios_base::beg ); + s >> ram_bytes_needed; + ram_bytes_needed += 256; // enough room to cover total_resources_table + } + + push_action( N(eosio), N(setalimits), mvo() + ("account", "eosio") + ("ram_bytes", ram_bytes_needed) + ("net_weight", -1) + ("cpu_weight", -1) + ); + + stake( N(alice1111111), N(eosio), core_sym::from_string("0.0000"), core_sym::from_string("1.0000") ); + + REQUIRE_MATCHING_OBJECT( get_total_stake( "eosio" ), mvo() + ("owner", "eosio") + ("net_weight", core_sym::from_string("0.0000")) + ("cpu_weight", core_sym::from_string("1.0000")) + ("ram_bytes", 0) + ); + + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "only supports unlimited accounts" ), + push_action( N(eosio), N(setalimits), mvo() + ("account", "eosio") + ("ram_bytes", ram_bytes_needed) + ("net_weight", -1) + ("cpu_weight", -1) + ) + ); + + BOOST_REQUIRE_EQUAL( error( "transaction net usage is too high: 128 > 0" ), + push_action( N(eosio), N(setalimits), mvo() + ("account", "eosio.saving") + ("ram_bytes", -1) + ("net_weight", -1) + ("cpu_weight", -1) + ) + ); + + BOOST_REQUIRE_EQUAL( success(), + push_action( N(eosio), N(setacctnet), mvo() + ("account", "eosio") + ("net_weight", -1) + ) + ); + + BOOST_REQUIRE_EQUAL( success(), + push_action( N(eosio), N(setacctcpu), mvo() + ("account", "eosio") + ("cpu_weight", -1) + + ) + ); + + BOOST_REQUIRE_EQUAL( success(), + push_action( N(eosio), N(setalimits), mvo() + ("account", "eosio.saving") + ("ram_bytes", ram_bytes_needed) + ("net_weight", -1) + ("cpu_weight", -1) + ) + ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( buy_pin_sell_ram, eosio_system_tester ) try { + BOOST_REQUIRE( get_total_stake( "eosio" ).is_null() ); + + transfer( N(eosio), N(alice1111111), core_sym::from_string("1020.0000") ); + + auto error_msg = stake( N(alice1111111), N(eosio), core_sym::from_string("10.0000"), core_sym::from_string("10.0000") ); + auto semicolon_pos = error_msg.find(';'); + + BOOST_REQUIRE_EQUAL( error("account eosio has insufficient ram"), + error_msg.substr(0, semicolon_pos) ); + + int64_t ram_bytes_needed = 0; + { + std::istringstream s( error_msg ); + s.seekg( semicolon_pos + 7, std::ios_base::beg ); + s >> ram_bytes_needed; + ram_bytes_needed += ram_bytes_needed/10; // enough buffer to make up for buyrambytes estimation errors + } + + auto alice_original_balance = get_balance( N(alice1111111) ); + + BOOST_REQUIRE_EQUAL( success(), buyrambytes( N(alice1111111), N(eosio), static_cast(ram_bytes_needed) ) ); + + auto tokens_paid_for_ram = alice_original_balance - get_balance( N(alice1111111) ); + + auto total_res = get_total_stake( "eosio" ); + + REQUIRE_MATCHING_OBJECT( total_res, mvo() + ("owner", "eosio") + ("net_weight", core_sym::from_string("0.0000")) + ("cpu_weight", core_sym::from_string("0.0000")) + ("ram_bytes", total_res["ram_bytes"].as_int64() ) + ); + + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "only supports unlimited accounts" ), + push_action( N(eosio), N(setalimits), mvo() + ("account", "eosio") + ("ram_bytes", ram_bytes_needed) + ("net_weight", -1) + ("cpu_weight", -1) + ) + ); + + BOOST_REQUIRE_EQUAL( success(), + push_action( N(eosio), N(setacctram), mvo() + ("account", "eosio") + ("ram_bytes", total_res["ram_bytes"].as_int64() ) + ) + ); + + auto eosio_original_balance = get_balance( N(eosio) ); + + BOOST_REQUIRE_EQUAL( success(), sellram( N(eosio), total_res["ram_bytes"].as_int64() ) ); + + auto tokens_received_by_selling_ram = get_balance( N(eosio) ) - eosio_original_balance; + + BOOST_REQUIRE( double(tokens_paid_for_ram.get_amount() - tokens_received_by_selling_ram.get_amount()) / tokens_paid_for_ram.get_amount() < 0.01 ); + +} FC_LOG_AND_RETHROW() + BOOST_AUTO_TEST_SUITE_END() From 9f60beead44ca4a33e671f3eab8e1e366fc3e761 Mon Sep 17 00:00:00 2001 From: arhag Date: Tue, 15 Jan 2019 14:49:19 -0500 Subject: [PATCH 07/23] update version and expand eosio version dependency range --- CMakeLists.txt | 2 +- tests/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 32c57283f..5426805cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(eosio_contracts) set(VERSION_MAJOR 1) set(VERSION_MINOR 6) set(VERSION_PATCH 0) -set(VERSION_SUFFIX rc1) +set(VERSION_SUFFIX rc2) if (VERSION_SUFFIX) set(VERSION_FULL "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}-${VERSION_SUFFIX}") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b973fa6d8..4660381a4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required( VERSION 3.5 ) set(EOSIO_VERSION_MIN "1.4") -set(EOSIO_VERSION_SOFT_MAX "1.5") +set(EOSIO_VERSION_SOFT_MAX "1.6") #set(EOSIO_VERSION_HARD_MAX "") find_package(eosio) From 060a9f891c0c766e964c7c1ff6b2cf0adab8f430 Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Tue, 15 Jan 2019 15:53:31 -0500 Subject: [PATCH 08/23] Fix sellrex bug --- contracts/eosio.system/src/rex.cpp | 18 ++++----- tests/eosio.system_tester.hpp | 5 +++ tests/eosio.system_tests.cpp | 64 +++++++++++++++++++++++++++++- 3 files changed, 76 insertions(+), 11 deletions(-) diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index cc1113b7d..44341b5e3 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -126,7 +126,7 @@ namespace eosiosystem { check( rex.amount <= bitr->matured_rex, "insufficient available rex" ); auto current_order = fill_rex_order( bitr, rex ); - update_rex_account( from, current_order.proceeds, current_order.stake_change ); + asset pending_sell_order = update_rex_account( from, current_order.proceeds, current_order.stake_change ); if ( !current_order.success ) { /** * REX order couldn't be filled and is added to queue. @@ -134,7 +134,7 @@ namespace eosiosystem { */ auto oitr = _rexorders.find( from.value ); if ( oitr == _rexorders.end() ) { - _rexorders.emplace( from, [&]( auto& order ) { + oitr = _rexorders.emplace( from, [&]( auto& order ) { order.owner = from; order.rex_requested = rex; order.is_open = true; @@ -145,11 +145,11 @@ namespace eosiosystem { } else { _rexorders.modify( oitr, same_payer, [&]( auto& order ) { order.rex_requested.amount += rex.amount; - check( order.rex_requested.amount <= bitr->matured_rex, - "insufficient funds for current and scheduled orders"); }); } + pending_sell_order.amount = oitr->rex_requested.amount; } + check( pending_sell_order.amount <= bitr->matured_rex, "insufficient funds for current and scheduled orders" ); // dummy action added so that sell order proceeds show up in action trace if ( current_order.success ) { dispatch_inline( null_account, "sellresult"_n, { }, std::make_tuple( current_order.proceeds ) ); @@ -575,7 +575,7 @@ namespace eosiosystem { { runrex(2); - check( rex_loans_available(), "rex loans are not currently available" ); + check( rex_loans_available(), "rex loans are currently not available" ); check( payment.symbol == core_symbol() && fund.symbol == core_symbol(), "must use core token" ); check( 0 < payment.amount && 0 <= fund.amount, "must use positive asset amount" ); @@ -735,7 +735,7 @@ namespace eosiosystem { { asset to_fund( proceeds ); asset to_stake( delta_stake ); - asset rex_in_sell_order( 0, core_symbol() ); + asset rex_in_sell_order( 0, rex_symbol ); auto itr = _rexorders.find( owner.value ); if ( itr != _rexorders.end() ) { if ( itr->is_open ) { @@ -839,7 +839,9 @@ namespace eosiosystem { total += rb.rex_maturities.front().second; rb.rex_maturities.pop_front(); } - rb.rex_maturities.emplace_back( get_rex_maturity(), total ); + if (total > 0 ) { + rb.rex_maturities.emplace_back( get_rex_maturity(), total ); + } }); } @@ -891,9 +893,7 @@ namespace eosiosystem { const int64_t S1 = S0 + payment.amount; const int64_t R0 = itr->total_rex.amount; const int64_t R1 = (uint128_t(S1) * R0) / S0; - rex_received.amount = R1 - R0; - _rexpool.modify( itr, same_payer, [&]( auto& rp ) { rp.total_lendable.amount = S1; rp.total_rex.amount = R1; diff --git a/tests/eosio.system_tester.hpp b/tests/eosio.system_tester.hpp index 0890efe02..1bde1f559 100644 --- a/tests/eosio.system_tester.hpp +++ b/tests/eosio.system_tester.hpp @@ -587,6 +587,11 @@ class eosio_system_tester : public TESTER { return abi_ser.binary_to_variant( "rex_order", data, abi_serializer_max_time ); } + fc::variant get_rex_order_obj( const account_name& act ) { + vector data = get_row_by_account( config::system_account_name, config::system_account_name, N(rexqueue), act ); + return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "rex_order", data, abi_serializer_max_time ); + } + fc::variant get_rex_pool() const { vector data; const auto& db = control->db(); diff --git a/tests/eosio.system_tests.cpp b/tests/eosio.system_tests.cpp index 82bdbbe7b..2853613ff 100644 --- a/tests/eosio.system_tests.cpp +++ b/tests/eosio.system_tests.cpp @@ -3653,6 +3653,66 @@ BOOST_FIXTURE_TEST_CASE( buy_rent_rex, eosio_system_tester ) try { } FC_LOG_AND_RETHROW() +BOOST_FIXTURE_TEST_CASE( buy_sell_sell_rex, eosio_system_tester ) try { + + const int64_t ratio = 10000; + const asset init_balance = core_sym::from_string("10000.0000"); + const asset init_net = core_sym::from_string("70.0000"); + const asset init_cpu = core_sym::from_string("90.0000"); + const std::vector accounts = { N(aliceaccount), N(bobbyaccount), N(carolaccount) }; + account_name alice = accounts[0], bob = accounts[1], carol = accounts[2]; + setup_rex_accounts( accounts, init_balance, init_net, init_cpu ); + + const int64_t init_cpu_limit = get_cpu_limit( alice ); + const int64_t init_net_limit = get_net_limit( alice ); + + // alice lends rex + const asset payment = core_sym::from_string("65.0000"); + BOOST_REQUIRE_EQUAL( success(), buyrex( alice, payment ) ); + BOOST_REQUIRE_EQUAL( success(), buyrex( bob, core_sym::from_string("0.0005") ) ); + BOOST_REQUIRE_EQUAL( init_balance - payment, get_rex_fund(alice) ); + auto rex_pool = get_rex_pool(); + const asset init_tot_unlent = rex_pool["total_unlent"].as(); + const asset init_tot_lendable = rex_pool["total_lendable"].as(); + const asset init_tot_rent = rex_pool["total_rent"].as(); + const int64_t init_stake = get_voter_info(alice)["staked"].as(); + BOOST_REQUIRE_EQUAL( core_sym::from_string("0.0000"), rex_pool["total_lent"].as() ); + BOOST_REQUIRE_EQUAL( ratio * init_tot_lendable.get_amount(), rex_pool["total_rex"].as().get_amount() ); + BOOST_REQUIRE_EQUAL( rex_pool["total_rex"].as(), get_rex_balance(alice) + get_rex_balance(bob) ); + + // bob rents cpu for carol + const asset fee = core_sym::from_string("7.0000"); + BOOST_REQUIRE_EQUAL( success(), rentcpu( bob, carol, fee ) ); + rex_pool = get_rex_pool(); + BOOST_REQUIRE_EQUAL( init_tot_lendable + fee, rex_pool["total_lendable"].as() ); + BOOST_REQUIRE_EQUAL( init_tot_rent + fee, rex_pool["total_rent"].as() ); + + produce_block( fc::days(5) ); + produce_blocks(2); + const asset rex_unit = asset::from_string("0.0001 REX"); + BOOST_REQUIRE_EQUAL( success(), sellrex( alice, get_rex_balance(alice) - rex_unit ) ); + BOOST_REQUIRE_EQUAL( false, get_rex_order_obj( alice ).is_null() ); + BOOST_REQUIRE_EQUAL( success(), sellrex( alice, rex_unit ) ); + BOOST_REQUIRE_EQUAL( sellrex( alice, rex_unit ), wasm_assert_msg("insufficient funds for current and scheduled orders") ); + BOOST_REQUIRE_EQUAL( ratio * payment.get_amount() - rex_unit.get_amount(), get_rex_order( alice )["rex_requested"].as().get_amount() ); + BOOST_REQUIRE_EQUAL( success(), consolidate( alice ) ); + BOOST_REQUIRE_EQUAL( 0, get_rex_balance_obj( alice )["rex_maturities"].get_array().size() ); + + produce_block( fc::days(26) ); + produce_blocks(2); + + BOOST_REQUIRE_EQUAL( success(), rexexec( alice, 2 ) ); + BOOST_REQUIRE_EQUAL( 0, get_rex_balance( alice ).get_amount() ); + BOOST_REQUIRE_EQUAL( 0, get_rex_balance_obj( alice )["matured_rex"].as() ); + const asset init_fund = get_rex_fund( alice ); + BOOST_REQUIRE_EQUAL( success(), updaterex( alice ) ); + BOOST_REQUIRE_EQUAL( 0, get_rex_balance( alice ).get_amount() ); + BOOST_REQUIRE_EQUAL( 0, get_rex_balance_obj( alice )["matured_rex"].as() ); + BOOST_REQUIRE ( init_fund < get_rex_fund( alice ) ); + +} FC_LOG_AND_RETHROW() + + BOOST_FIXTURE_TEST_CASE( buy_sell_claim_rex, eosio_system_tester ) try { auto within_one = [](int64_t a, int64_t b) -> bool { return std::abs( a - b ) <= 1; }; @@ -3730,7 +3790,7 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_claim_rex, eosio_system_tester ) try { // alices's order is still open // an action is needed to trigger queue processing produce_block( fc::hours(2) ); - BOOST_REQUIRE_EQUAL( wasm_assert_msg("rex loans are not currently available"), + BOOST_REQUIRE_EQUAL( wasm_assert_msg("rex loans are currently not available"), rentcpu( frank, frank, core_sym::from_string("0.0001") ) ); { auto trace = base_tester::push_action( N(eosio), N(buyrex), frank, @@ -4298,7 +4358,7 @@ BOOST_FIXTURE_TEST_CASE( close_rex, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( 0, get_rex_pool()["total_rex"].as().get_amount() ); BOOST_REQUIRE_EQUAL( 0, get_rex_pool()["total_lendable"].as().get_amount() ); - BOOST_REQUIRE_EQUAL( wasm_assert_msg("rex loans are not currently available"), + BOOST_REQUIRE_EQUAL( wasm_assert_msg("rex loans are currently not available"), rentcpu( frank, frank, core_sym::from_string("1.0000") ) ); } FC_LOG_AND_RETHROW() From 50b235c6a327e05e21699b2e837b81db26bd6d7b Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Tue, 22 Jan 2019 15:21:59 -0500 Subject: [PATCH 09/23] Fix bug in updating REX vote stake --- .../include/eosio.system/eosio.system.hpp | 33 ++++++++ .../eosio.system/src/delegate_bandwidth.cpp | 13 ++-- contracts/eosio.system/src/rex.cpp | 34 ++++++++- contracts/eosio.system/src/voting.cpp | 3 +- tests/eosio.system_tests.cpp | 76 ++++++++++++++++++- 5 files changed, 145 insertions(+), 14 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index cd2dab444..d25af39f8 100755 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -567,6 +567,7 @@ namespace eosiosystem { void process_rex_maturities( const rex_balance_table::const_iterator& bitr ); void consolidate_rex_balance( const rex_balance_table::const_iterator& bitr, const asset& rex_in_sell_order ); + void update_rex_stake( const name& voter ); // defined in delegate_bandwidth.cpp void changebw( name from, name receiver, @@ -582,6 +583,38 @@ namespace eosiosystem { double shares_rate, bool reset_to_zero = false ); double update_total_votepay_share( time_point ct, double additional_shares_delta = 0.0, double shares_rate_delta = 0.0 ); + + template + class registration { + public: + template + struct for_each { + template + static constexpr void call( system_contract* this_contract, Args&&... args ) + { + std::invoke( P, this_contract, std::forward(args)... ); + for_each::call( this_contract, std::forward(args)... ); + } + }; + template + struct for_each

{ + template + static constexpr void call( system_contract* this_contract, Args&&... args ) + { + std::invoke( P, this_contract, std::forward(args)... ); + } + }; + + template + constexpr void operator() ( Args&&... args ) + { + for_each::call( this_contract, std::forward(args)... ); + } + + system_contract* this_contract; + }; + + registration<&system_contract::update_rex_stake> vote_stake_updater{ this }; }; } /// eosiosystem diff --git a/contracts/eosio.system/src/delegate_bandwidth.cpp b/contracts/eosio.system/src/delegate_bandwidth.cpp index 1b574da3a..14c7bd640 100755 --- a/contracts/eosio.system/src/delegate_bandwidth.cpp +++ b/contracts/eosio.system/src/delegate_bandwidth.cpp @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -22,7 +21,6 @@ namespace eosiosystem { using eosio::asset; using eosio::indexed_by; using eosio::const_mem_fun; - using eosio::print; using eosio::permission_level; using eosio::time_point_sec; using std::map; @@ -233,8 +231,8 @@ namespace eosiosystem { require_auth( from ); check( stake_net_delta.amount != 0 || stake_cpu_delta.amount != 0, "should stake non-zero amount" ); check( std::abs( (stake_net_delta + stake_cpu_delta).amount ) - >= std::max( std::abs( stake_net_delta.amount ), std::abs( stake_cpu_delta.amount ) ), - "net and cpu deltas cannot be opposite signs" ); + >= std::max( std::abs( stake_net_delta.amount ), std::abs( stake_cpu_delta.amount ) ), + "net and cpu deltas cannot be opposite signs" ); name source_stake_from = from; if ( transfer ) { @@ -385,6 +383,7 @@ namespace eosiosystem { } } + vote_stake_updater( from ); update_voting_power( from, stake_net_delta + stake_cpu_delta ); } @@ -402,7 +401,7 @@ namespace eosiosystem { }); } - check( 0 <= voter_itr->staked, "stake for voting cannot be negative"); + check( 0 <= voter_itr->staked, "stake for voting cannot be negative" ); if( voter == "b1"_n ) { validate_b1_vesting( voter_itr->staked ); @@ -434,7 +433,7 @@ namespace eosiosystem { check( unstake_net_quantity >= zero_asset, "must unstake a positive amount" ); check( unstake_cpu_quantity.amount + unstake_net_quantity.amount > 0, "must unstake a positive amount" ); check( _gstate.total_activated_stake >= min_activated_stake, - "cannot undelegate bandwidth until the chain is activated (at least 15% of all tokens participate in voting)" ); + "cannot undelegate bandwidth until the chain is activated (at least 15% of all tokens participate in voting)" ); changebw( from, receiver, -unstake_net_quantity, -unstake_cpu_quantity, false); } // undelegatebw @@ -447,7 +446,7 @@ namespace eosiosystem { auto req = refunds_tbl.find( owner.value ); check( req != refunds_tbl.end(), "refund request not found" ); check( req->request_time + seconds(refund_delay_sec) <= current_time_point(), - "refund is not available yet" ); + "refund is not available yet" ); INLINE_ACTION_SENDER(eosio::token, transfer)( token_account, { {stake_account, active_permission}, {req->owner, active_permission} }, diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index 44341b5e3..c16592ae8 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -4,6 +4,8 @@ #include +#include + namespace eosiosystem { /** @@ -23,6 +25,7 @@ namespace eosiosystem { transfer_to_fund( owner, amount ); update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); } + /** * @brief Withdraws SYS tokens from user REX fund * @@ -623,13 +626,13 @@ namespace eosiosystem { const int64_t S1 = (uint128_t(R1) * S0) / R0; asset proceeds( S0 - S1, core_symbol() ); asset stake_change( 0, core_symbol() ); - bool success = false; + bool success = false; const int64_t unlent_lower_bound = ( uint128_t(2) * rexitr->total_lent.amount ) / 10; const int64_t available_unlent = rexitr->total_unlent.amount - unlent_lower_bound; // available_unlent <= 0 is possible - if ( proceeds.amount <= available_unlent ) { + if ( proceeds.amount <= available_unlent ) { const int64_t init_vote_stake_amount = bitr->vote_stake.amount; - const int64_t current_stake_value = ( uint128_t(bitr->rex_balance.amount) * S0 ) / R0; + const int64_t current_stake_value = ( uint128_t(bitr->rex_balance.amount) * S0 ) / R0; _rexpool.modify( rexitr, same_payer, [&]( auto& rt ) { rt.total_rex.amount = R1; rt.total_lendable.amount = S1; @@ -949,4 +952,29 @@ namespace eosiosystem { return current_rex_stake - init_rex_stake; } + void system_contract::update_rex_stake( const name& voter ) + { + int64_t delta_stake = 0; + auto bitr = _rexbalance.find( voter.value ); + if ( bitr != _rexbalance.end() && rex_available() ) { + asset init_vote_stake = bitr->vote_stake; + asset current_vote_stake( 0, core_symbol() ); + current_vote_stake.amount = ( uint128_t(bitr->rex_balance.amount) * _rexpool.begin()->total_lendable.amount ) + / _rexpool.begin()->total_rex.amount; + _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { + rb.vote_stake.amount = current_vote_stake.amount; + }); + delta_stake = current_vote_stake.amount - init_vote_stake.amount; + } + + if ( delta_stake != 0 ) { + auto vitr = _voters.find( voter.value ); + if ( vitr != _voters.end() ) { + _voters.modify( vitr, same_payer, [&]( auto& vinfo ) { + vinfo.staked += delta_stake; + }); + } + } + } + }; /// namespace eosiosystem diff --git a/contracts/eosio.system/src/voting.cpp b/contracts/eosio.system/src/voting.cpp index a84c9f5ad..3d9016be4 100755 --- a/contracts/eosio.system/src/voting.cpp +++ b/contracts/eosio.system/src/voting.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -21,7 +20,6 @@ namespace eosiosystem { using eosio::indexed_by; using eosio::const_mem_fun; - using eosio::print; using eosio::singleton; using eosio::transaction; @@ -194,6 +192,7 @@ namespace eosiosystem { */ void system_contract::voteproducer( const name voter_name, const name proxy, const std::vector& producers ) { require_auth( voter_name ); + vote_stake_updater( voter_name ); update_votes( voter_name, proxy, producers, true ); auto rex_itr = _rexbalance.find( voter_name.value ); if( rex_itr != _rexbalance.end() && rex_itr->rex_balance.amount > 0 ) { diff --git a/tests/eosio.system_tests.cpp b/tests/eosio.system_tests.cpp index 2853613ff..9824f1a63 100644 --- a/tests/eosio.system_tests.cpp +++ b/tests/eosio.system_tests.cpp @@ -3729,9 +3729,10 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_claim_rex, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( success(), buyrex( alice, purchase1) ); BOOST_REQUIRE_EQUAL( success(), buyrex( bob, purchase2) ); BOOST_REQUIRE_EQUAL( success(), buyrex( carol, purchase3) ); - + BOOST_REQUIRE_EQUAL( init_balance - purchase1, get_rex_fund(alice) ); BOOST_REQUIRE_EQUAL( purchase1.get_amount(), get_voter_info(alice)["staked"].as() - init_stake ); + BOOST_REQUIRE_EQUAL( init_balance - purchase2, get_rex_fund(bob) ); BOOST_REQUIRE_EQUAL( init_balance - purchase3, get_rex_fund(carol) ); @@ -3828,7 +3829,7 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_claim_rex, eosio_system_tester ) try { auto output = get_rexorder_result( trace ); BOOST_REQUIRE_EQUAL( output.size(), 0 ); } - + } FC_LOG_AND_RETHROW() @@ -4234,6 +4235,77 @@ BOOST_FIXTURE_TEST_CASE( update_rex, eosio_system_tester, * boost::unit_test::to } FC_LOG_AND_RETHROW() +BOOST_FIXTURE_TEST_CASE( update_rex_vote, eosio_system_tester, * boost::unit_test::tolerance(1e-10) ) try { + + cross_15_percent_threshold(); + + // create accounts {defproducera, defproducerb, ..., defproducerz} and register as producers + std::vector producer_names; + { + producer_names.reserve('z' - 'a' + 1); + const std::string root("defproducer"); + for ( char c = 'a'; c <= 'z'; ++c ) { + producer_names.emplace_back(root + std::string(1, c)); + } + + setup_producer_accounts(producer_names); + for ( const auto& p: producer_names ) { + BOOST_REQUIRE_EQUAL( success(), regproducer(p) ); + BOOST_TEST_REQUIRE( 0 == get_producer_info(p)["total_votes"].as() ); + } + } + + const asset init_balance = core_sym::from_string("10000.0000"); + const std::vector accounts = { N(aliceaccount), N(bobbyaccount), N(carolaccount), N(emilyaccount), N(frankaccount) }; + account_name alice = accounts[0], bob = accounts[1], carol = accounts[2], emily = accounts[3], frank = accounts[4]; + setup_rex_accounts( accounts, init_balance ); + + const int64_t init_stake_amount = get_voter_info( alice )["staked"].as(); + const asset init_stake( init_stake_amount, symbol{CORE_SYM} ); + + const asset purchase = core_sym::from_string("250.0000"); + BOOST_REQUIRE_EQUAL( success(), buyrex( alice, core_sym::from_string("250.0000") ) ); + BOOST_REQUIRE_EQUAL( purchase, get_rex_pool()["total_lendable"].as() ); + BOOST_REQUIRE_EQUAL( purchase, get_rex_vote_stake(alice) ); + BOOST_REQUIRE_EQUAL( get_rex_vote_stake(alice).get_amount(), get_voter_info(alice)["staked"].as() - init_stake_amount ); + BOOST_REQUIRE_EQUAL( purchase, get_rex_pool()["total_lendable"].as() ); + + BOOST_REQUIRE_EQUAL( success(), + vote( alice, std::vector(producer_names.begin(), producer_names.begin() + 21) ) ); + BOOST_REQUIRE_EQUAL( purchase, get_rex_vote_stake(alice) ); + BOOST_REQUIRE_EQUAL( purchase.get_amount(), get_voter_info(alice)["staked"].as() - init_stake_amount ); + + const auto init_rex_pool = get_rex_pool(); + const asset rent = core_sym::from_string("25.0000"); + BOOST_REQUIRE_EQUAL( success(), rentcpu( frank, bob, rent ) ); + const auto curr_rex_pool = get_rex_pool(); + BOOST_REQUIRE_EQUAL( curr_rex_pool["total_lendable"].as(), init_rex_pool["total_lendable"].as() + rent ); + BOOST_REQUIRE_EQUAL( success(), + vote( alice, std::vector(producer_names.begin(), producer_names.begin() + 21) ) ); + BOOST_REQUIRE_EQUAL( (purchase + rent).get_amount(), get_voter_info(alice)["staked"].as() - init_stake_amount ); + BOOST_REQUIRE_EQUAL( purchase + rent, get_rex_vote_stake(alice) ); + BOOST_TEST_REQUIRE ( stake2votes(purchase + rent + init_stake) == + get_producer_info(producer_names[0])["total_votes"].as_double() ); + BOOST_TEST_REQUIRE ( stake2votes(purchase + rent + init_stake) == + get_producer_info(producer_names[20])["total_votes"].as_double() ); + + const asset to_net_stake = core_sym::from_string("60.0000"); + const asset to_cpu_stake = core_sym::from_string("40.0000"); + transfer( config::system_account_name, alice, to_net_stake + to_cpu_stake, config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), rentcpu( frank, bob, rent ) ); + BOOST_REQUIRE_EQUAL( success(), stake( alice, alice, to_net_stake, to_cpu_stake ) ); + BOOST_REQUIRE_EQUAL( purchase + rent + rent, get_rex_vote_stake(alice) ); + BOOST_TEST_REQUIRE ( stake2votes(init_stake + purchase + rent + rent + to_net_stake + to_cpu_stake) == + get_producer_info(producer_names[0])["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( success(), rentcpu( frank, bob, rent ) ); + BOOST_REQUIRE_EQUAL( success(), unstake( alice, alice, to_net_stake, to_cpu_stake ) ); + BOOST_REQUIRE_EQUAL( purchase + rent + rent + rent, get_rex_vote_stake(alice) ); + BOOST_TEST_REQUIRE ( stake2votes(init_stake + purchase + rent + rent + rent) == + get_producer_info(producer_names[0])["total_votes"].as_double() ); + +} FC_LOG_AND_RETHROW() + + BOOST_FIXTURE_TEST_CASE( deposit_rex_fund, eosio_system_tester ) try { const asset init_balance = core_sym::from_string("1000.0000"); From fa5936faf49a5b2a000981b111e08529cf556c69 Mon Sep 17 00:00:00 2001 From: arhag Date: Tue, 22 Jan 2019 17:52:33 -0500 Subject: [PATCH 10/23] fix bug found from @zorba80's review --- contracts/eosio.system/src/rex.cpp | 62 +++++++++++++++--------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index ba98a09cb..f756f3561 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -182,7 +182,7 @@ namespace eosiosystem { int64_t rented_tokens = rent_rex( cpu_loans, from, receiver, loan_payment, loan_fund ); update_resource_limits( from, receiver, 0, rented_tokens ); } - + /** * Rents as many SYS tokens as determined by market price and stakes them for NET bandwidth * for the benefit of receiver account. After 30 days the rented SYS delegation of NET will @@ -241,12 +241,12 @@ namespace eosiosystem { * * @param from - loan creator * @param loan_num - loan id - * @param amount - tokens to be withdrawn from loan fund + * @param amount - tokens to be withdrawn from loan fund */ void system_contract::defcpuloan( const name& from, uint64_t loan_num, const asset& amount ) { require_auth( from ); - + rex_cpu_loan_table cpu_loans( _self, _self.value ); defund_rex_loan( cpu_loans, from, loan_num, amount ); } @@ -269,7 +269,7 @@ namespace eosiosystem { /** * @brief Updates REX owner vote weight to current value of held REX tokens * - * @param owner - owner of REX tokens + * @param owner - owner of REX tokens */ void system_contract::updaterex( const name& owner ) { @@ -284,7 +284,7 @@ namespace eosiosystem { const int64_t total_rex = rexp_itr->total_rex.amount; const int64_t total_lendable = rexp_itr->total_lendable.amount; const int64_t rex_balance = itr->rex_balance.amount; - + asset current_stake( 0, core_symbol() ); if ( total_rex > 0 ) { current_stake.amount = ( uint128_t(rex_balance) * total_lendable ) / total_rex; @@ -307,7 +307,7 @@ namespace eosiosystem { void system_contract::rexexec( const name& user, uint16_t max ) { require_auth( user ); - + runrex( max ); } @@ -320,9 +320,9 @@ namespace eosiosystem { void system_contract::consolidate( const name& owner ) { require_auth( owner ); - + runrex(2); - + auto bitr = _rexbalance.require_find( owner.value, "account has no REX balance" ); asset rex_in_sell_order = update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); consolidate_rex_balance( bitr, rex_in_sell_order ); @@ -336,12 +336,12 @@ namespace eosiosystem { void system_contract::closerex( const name& owner ) { require_auth( owner ); - + if ( rex_system_initialized() ) runrex(2); - + update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); - + /// check for any outstanding loans or rex fund { rex_cpu_loan_table cpu_loans( _self, _self.value ); @@ -354,7 +354,7 @@ namespace eosiosystem { auto fund_itr = _rexfunds.find( owner.value ); bool no_outstanding_rex_fund = ( fund_itr != _rexfunds.end() ) && ( fund_itr->balance.amount == 0 ); - + if ( no_outstanding_cpu_loans && no_outstanding_net_loans && no_outstanding_rex_fund ) { _rexfunds.erase( fund_itr ); } @@ -428,10 +428,6 @@ namespace eosiosystem { check( 0 <= tot_itr->net_weight.amount, "insufficient staked total net bandwidth" ); check( 0 <= tot_itr->cpu_weight.amount, "insufficient staked total cpu bandwidth" ); - if ( tot_itr->is_empty() ) { - totals_tbl.erase( tot_itr ); - } - { bool net_managed = false; bool cpu_managed = false; @@ -452,6 +448,10 @@ namespace eosiosystem { cpu_managed ? cpu : tot_itr->cpu_weight.amount ); } } + + if ( tot_itr->is_empty() ) { + totals_tbl.erase( tot_itr ); + } } void system_contract::check_voting_requirement( const name& owner, const char* error_msg )const @@ -504,7 +504,7 @@ namespace eosiosystem { transfer_to_fund( itr->from, itr->balance ); } } - + return { delete_loan, delta_stake }; }; @@ -521,9 +521,9 @@ namespace eosiosystem { rex_cpu_loan_table cpu_loans( _self, _self.value ); auto cpu_idx = cpu_loans.get_index<"byexpr"_n>(); for ( uint16_t i = 0; i < max; ++i ) { - auto itr = cpu_idx.begin(); + auto itr = cpu_idx.begin(); if ( itr == cpu_idx.end() || itr->expiration > current_time_point() ) break; - + auto result = process_expired_loan( cpu_idx, itr ); if ( result.second != 0 ) update_resource_limits( itr->from, itr->receiver, 0, result.second ); @@ -715,7 +715,7 @@ namespace eosiosystem { _rexfunds.emplace( owner, [&]( auto& fund ) { fund.owner = owner; fund.balance = amount; - }); + }); } else { _rexfunds.modify( itr, same_payer, [&]( auto& fund ) { fund.balance.amount += amount.amount; @@ -734,9 +734,9 @@ namespace eosiosystem { * @param owner - owner account name * @param proceeds - additional proceeds to be transfered to owner REX fund * @param delta_stake - additional stake to be added to owner vote weight - * @param force_vote_update - if true, vote weight is updated even if vote stake didn't change + * @param force_vote_update - if true, vote weight is updated even if vote stake didn't change * - * @return asset - REX amount of owner unfilled sell order if one exists + * @return asset - REX amount of owner unfilled sell order if one exists */ asset system_contract::update_rex_account( const name& owner, const asset& proceeds, const asset& delta_stake, bool force_vote_update ) { @@ -765,7 +765,7 @@ namespace eosiosystem { /** * @brief Channels system fees to REX pool * - * @param from - account from which asset is transfered to REX pool + * @param from - account from which asset is transfered to REX pool * @param amount - amount of tokens to be transfered */ void system_contract::channel_to_rex( const name& from, const asset& amount ) @@ -834,10 +834,10 @@ namespace eosiosystem { * @brief Consolidates REX maturity buckets into one * * @param bitr - iterator pointing to rex_balance object - * @param rex_in_sell_order - REX tokens in owner unfilled sell order, if one exists + * @param rex_in_sell_order - REX tokens in owner unfilled sell order, if one exists */ void system_contract::consolidate_rex_balance( const rex_balance_table::const_iterator& bitr, - const asset& rex_in_sell_order ) + const asset& rex_in_sell_order ) { _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { int64_t total = rb.matured_rex - rex_in_sell_order.amount; @@ -860,12 +860,12 @@ namespace eosiosystem { asset system_contract::add_to_rex_pool( const asset& payment ) { /** - * If CORE_SYMBOL is (EOS,4), maximum supply is 10^10 tokens (10 billion tokens), i.e., maximum amount + * If CORE_SYMBOL is (EOS,4), maximum supply is 10^10 tokens (10 billion tokens), i.e., maximum amount * of indivisible units is 10^14. rex_ratio = 10^4 sets the upper bound on (REX,4) indivisible units to - * 10^18 and that is within the maximum allowable amount field of asset type which is set to 2^62 + * 10^18 and that is within the maximum allowable amount field of asset type which is set to 2^62 * (approximately 4.6 * 10^18). For a different CORE_SYMBOL, and in order for maximum (REX,4) amount not * to exceed that limit, maximum amount of indivisible units cannot be set to a value larger than 4 * 10^14. - * If precision of CORE_SYMBOL is 4, that corresponds to a maximum supply of 40 billion tokens. + * If precision of CORE_SYMBOL is 4, that corresponds to a maximum supply of 40 billion tokens. */ const int64_t rex_ratio = 10000; const int64_t init_total_rent = 100'000'0000; /// base amount prevents renting profitably until at least a minimum number of core_symbol() is made available @@ -910,7 +910,7 @@ namespace eosiosystem { check( rp.total_unlent.amount >= 0, "programmer error, this should never go negative" ); }); } - + return rex_received; } @@ -919,7 +919,7 @@ namespace eosiosystem { * * @param owner - account name of REX owner * @param payment - amount core tokens paid to buy REX - * @param rex_received - amount of purchased REX tokens + * @param rex_received - amount of purchased REX tokens * * @return asset - change in owner REX vote stake */ @@ -939,7 +939,7 @@ namespace eosiosystem { init_rex_stake.amount = bitr->vote_stake.amount; _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { rb.rex_balance.amount += rex_received.amount; - rb.vote_stake.amount = ( uint128_t(rb.rex_balance.amount) * _rexpool.begin()->total_lendable.amount ) + rb.vote_stake.amount = ( uint128_t(rb.rex_balance.amount) * _rexpool.begin()->total_lendable.amount ) / _rexpool.begin()->total_rex.amount; }); current_rex_stake.amount = bitr->vote_stake.amount; From 7a6b11477a693636e9ee3f09d6d055a68a539e2c Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Fri, 25 Jan 2019 15:35:20 -0500 Subject: [PATCH 11/23] REX rounding errors --- contracts/eosio.system/src/rex.cpp | 7 ++-- tests/eosio.system_tests.cpp | 53 +++++++++++++++++++++++------- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index 71012c52f..72782e800 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -637,12 +637,15 @@ namespace eosiosystem { auto rexitr = _rexpool.begin(); const int64_t S0 = rexitr->total_lendable.amount; const int64_t R0 = rexitr->total_rex.amount; + const int64_t p = (uint128_t(rex.amount) * S0) / R0; const int64_t R1 = R0 - rex.amount; - const int64_t S1 = (uint128_t(R1) * S0) / R0; - asset proceeds( S0 - S1, core_symbol() ); + const int64_t S1 = S0 - p; + asset proceeds( p, core_symbol() ); asset stake_change( 0, core_symbol() ); bool success = false; + check( proceeds.amount > 0, "proceeds are negligible" ); + const int64_t unlent_lower_bound = ( uint128_t(2) * rexitr->total_lent.amount ) / 10; const int64_t available_unlent = rexitr->total_unlent.amount - unlent_lower_bound; // available_unlent <= 0 is possible if ( proceeds.amount <= available_unlent ) { diff --git a/tests/eosio.system_tests.cpp b/tests/eosio.system_tests.cpp index bb41ef110..d7b2a0d2a 100644 --- a/tests/eosio.system_tests.cpp +++ b/tests/eosio.system_tests.cpp @@ -3437,12 +3437,11 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_rex, eosio_system_tester ) try { auto init_total_rex = rex_pool["total_rex"].as().get_amount(); auto init_total_lendable = rex_pool["total_lendable"].as().get_amount(); - BOOST_REQUIRE_EQUAL( success(), sellrex( bob, asset::from_string("550000.6800 REX") ) ); - BOOST_REQUIRE_EQUAL( asset::from_string("199999.3200 REX"), get_rex_balance(bob) ); + BOOST_REQUIRE_EQUAL( success(), sellrex( bob, asset::from_string("550001.0000 REX") ) ); + BOOST_REQUIRE_EQUAL( asset::from_string("199999.0000 REX"), get_rex_balance(bob) ); rex_pool = get_rex_pool(); auto total_rex = rex_pool["total_rex"].as().get_amount(); auto total_lendable = rex_pool["total_lendable"].as().get_amount(); - BOOST_REQUIRE_EQUAL( init_total_rex / init_total_lendable, total_rex / total_lendable ); BOOST_REQUIRE_EQUAL( total_lendable, rex_pool["total_unlent"].as().get_amount() ); BOOST_REQUIRE_EQUAL( core_sym::from_string("0.0000"), rex_pool["total_lent"].as() ); @@ -3452,6 +3451,38 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_rex, eosio_system_tester ) try { } FC_LOG_AND_RETHROW() +BOOST_FIXTURE_TEST_CASE( buy_sell_small_rex, eosio_system_tester ) try { + + const int64_t ratio = 10000; + const asset init_rent = core_sym::from_string("100000.0000"); + const asset init_balance = core_sym::from_string("1000.0000"); + const std::vector accounts = { N(aliceaccount), N(bobbyaccount), N(carolaccount) }; + account_name alice = accounts[0], bob = accounts[1], carol = accounts[2]; + setup_rex_accounts( accounts, init_balance ); + + const asset payment = core_sym::from_string("10.0000"); + BOOST_REQUIRE_EQUAL( ratio * payment.get_amount(), get_buyrex_result( alice, payment ).get_amount() ); + + produce_blocks(2); + produce_block( fc::days(5) ); + produce_blocks(2); + + asset init_rex_stake = get_rex_vote_stake( alice ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("proceeds are negligible"), sellrex( alice, asset::from_string("0.0001 REX") ) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("proceeds are negligible"), sellrex( alice, asset::from_string("0.9999 REX") ) ); + BOOST_REQUIRE_EQUAL( core_sym::from_string("0.0001"), get_sellrex_result( alice, asset::from_string("1.0000 REX") ) ); + BOOST_REQUIRE_EQUAL( core_sym::from_string("0.0001"), get_sellrex_result( alice, asset::from_string("1.9999 REX") ) ); + BOOST_REQUIRE_EQUAL( core_sym::from_string("0.0002"), get_sellrex_result( alice, asset::from_string("2.0000 REX") ) ); + BOOST_REQUIRE_EQUAL( core_sym::from_string("0.0002"), get_sellrex_result( alice, asset::from_string("2.9999 REX") ) ); + BOOST_REQUIRE_EQUAL( get_rex_vote_stake( alice ), init_rex_stake - core_sym::from_string("0.0006") ); + + BOOST_REQUIRE_EQUAL( success(), rentcpu( carol, bob, core_sym::from_string("10.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), sellrex( alice, asset::from_string("0.9000 REX") ) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("proceeds are negligible"), sellrex( alice, asset::from_string("0.4000 REX") ) ); + +} FC_LOG_AND_RETHROW() + + BOOST_FIXTURE_TEST_CASE( unstake_buy_rex, eosio_system_tester, * boost::unit_test::tolerance(1e-10) ) try { const int64_t ratio = 10000; @@ -3690,14 +3721,14 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_sell_rex, eosio_system_tester ) try { produce_block( fc::days(5) ); produce_blocks(2); - const asset rex_unit = asset::from_string("0.0001 REX"); - BOOST_REQUIRE_EQUAL( success(), sellrex( alice, get_rex_balance(alice) - rex_unit ) ); - BOOST_REQUIRE_EQUAL( false, get_rex_order_obj( alice ).is_null() ); - BOOST_REQUIRE_EQUAL( success(), sellrex( alice, rex_unit ) ); - BOOST_REQUIRE_EQUAL( sellrex( alice, rex_unit ), wasm_assert_msg("insufficient funds for current and scheduled orders") ); - BOOST_REQUIRE_EQUAL( ratio * payment.get_amount() - rex_unit.get_amount(), get_rex_order( alice )["rex_requested"].as().get_amount() ); - BOOST_REQUIRE_EQUAL( success(), consolidate( alice ) ); - BOOST_REQUIRE_EQUAL( 0, get_rex_balance_obj( alice )["rex_maturities"].get_array().size() ); + const asset rex_tok = asset::from_string("1.0000 REX"); + BOOST_REQUIRE_EQUAL( success(), sellrex( alice, get_rex_balance(alice) - rex_tok ) ); + BOOST_REQUIRE_EQUAL( false, get_rex_order_obj( alice ).is_null() ); + BOOST_REQUIRE_EQUAL( success(), sellrex( alice, rex_tok ) ); + BOOST_REQUIRE_EQUAL( sellrex( alice, rex_tok ), wasm_assert_msg("insufficient funds for current and scheduled orders") ); + BOOST_REQUIRE_EQUAL( ratio * payment.get_amount() - rex_tok.get_amount(), get_rex_order( alice )["rex_requested"].as().get_amount() ); + BOOST_REQUIRE_EQUAL( success(), consolidate( alice ) ); + BOOST_REQUIRE_EQUAL( 0, get_rex_balance_obj( alice )["rex_maturities"].get_array().size() ); produce_block( fc::days(26) ); produce_blocks(2); From e0676472fac96bf0010d20a4fb61988d25930b0c Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Tue, 29 Jan 2019 16:09:17 -0500 Subject: [PATCH 12/23] Add comment --- contracts/eosio.system/src/rex.cpp | 5 +++++ tests/eosio.system_tests.cpp | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index 72782e800..ff23b256d 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -970,6 +970,11 @@ namespace eosiosystem { return current_rex_stake - init_rex_stake; } + /** + * @brief Updates voter REX vote stake to the current value of REX tokens held + * + * @param voter - account name of voter + */ void system_contract::update_rex_stake( const name& voter ) { int64_t delta_stake = 0; diff --git a/tests/eosio.system_tests.cpp b/tests/eosio.system_tests.cpp index d7b2a0d2a..6276b2d2c 100644 --- a/tests/eosio.system_tests.cpp +++ b/tests/eosio.system_tests.cpp @@ -3402,7 +3402,7 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_rex, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( asset::from_string("25000.0000 REX"), get_buyrex_result( alice, core_sym::from_string("2.5000") ) ); produce_blocks(2); produce_block(fc::days(5)); - BOOST_REQUIRE_EQUAL( core_sym::from_string("2.5000"), get_sellrex_result( alice, asset::from_string("25000.0000 REX") ) ); + BOOST_REQUIRE_EQUAL( core_sym::from_string("2.5000"), get_sellrex_result( alice, asset::from_string("25000.0000 REX") ) ); BOOST_REQUIRE_EQUAL( success(), buyrex( alice, core_sym::from_string("13.0000") ) ); BOOST_REQUIRE_EQUAL( core_sym::from_string("13.0000"), get_rex_vote_stake( alice ) ); From a4fd33e6e2dea204c9e05729253a580387a7d214 Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Wed, 30 Jan 2019 18:52:11 -0500 Subject: [PATCH 13/23] Change initial REX connector balance, unlent REX pool lower bound --- contracts/eosio.system/src/rex.cpp | 4 ++-- tests/eosio.system_tests.cpp | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index ff23b256d..b1450eeb6 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -646,7 +646,7 @@ namespace eosiosystem { check( proceeds.amount > 0, "proceeds are negligible" ); - const int64_t unlent_lower_bound = ( uint128_t(2) * rexitr->total_lent.amount ) / 10; + const int64_t unlent_lower_bound = rexitr->total_lent.amount; const int64_t available_unlent = rexitr->total_unlent.amount - unlent_lower_bound; // available_unlent <= 0 is possible if ( proceeds.amount <= available_unlent ) { const int64_t init_vote_stake_amount = bitr->vote_stake.amount; @@ -884,7 +884,7 @@ namespace eosiosystem { * If precision of CORE_SYMBOL is 4, that corresponds to a maximum supply of 40 billion tokens. */ const int64_t rex_ratio = 10000; - const int64_t init_total_rent = 100'000'0000; /// base amount prevents renting profitably until at least a minimum number of core_symbol() is made available + const int64_t init_total_rent = 20'000'0000; /// base amount prevents renting profitably until at least a minimum number of core_symbol() is made available asset rex_received( 0, rex_symbol ); auto itr = _rexpool.begin(); if ( !rex_system_initialized() ) { diff --git a/tests/eosio.system_tests.cpp b/tests/eosio.system_tests.cpp index 6276b2d2c..21a54eb4a 100644 --- a/tests/eosio.system_tests.cpp +++ b/tests/eosio.system_tests.cpp @@ -3393,7 +3393,7 @@ BOOST_FIXTURE_TEST_CASE( ram_gift, eosio_system_tester ) try { BOOST_FIXTURE_TEST_CASE( buy_sell_rex, eosio_system_tester ) try { const int64_t ratio = 10000; - const asset init_rent = core_sym::from_string("100000.0000"); + const asset init_rent = core_sym::from_string("20000.0000"); const asset init_balance = core_sym::from_string("1000.0000"); const std::vector accounts = { N(aliceaccount), N(bobbyaccount), N(carolaccount), N(emilyaccount), N(frankaccount) }; account_name alice = accounts[0], bob = accounts[1], carol = accounts[2], emily = accounts[3], frank = accounts[4]; @@ -3754,7 +3754,7 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_claim_rex, eosio_system_tester ) try { account_name alice = accounts[0], bob = accounts[1], carol = accounts[2], emily = accounts[3], frank = accounts[4]; setup_rex_accounts( accounts, init_balance ); - const auto purchase1 = core_sym::from_string("880000.0000"); + const auto purchase1 = core_sym::from_string("100000.0000"); const auto purchase2 = core_sym::from_string("471000.0000"); const auto purchase3 = core_sym::from_string("469000.0000"); const auto init_stake = get_voter_info(alice)["staked"].as(); @@ -3772,7 +3772,7 @@ BOOST_FIXTURE_TEST_CASE( buy_sell_claim_rex, eosio_system_tester ) try { auto init_bob_rex = get_rex_balance(bob); auto init_carol_rex = get_rex_balance(carol); - BOOST_REQUIRE_EQUAL(core_sym::from_string("100000.0000"), get_rex_pool()["total_rent"].as() ); + BOOST_REQUIRE_EQUAL(core_sym::from_string("20000.0000"), get_rex_pool()["total_rent"].as() ); const asset rent_payment = core_sym::from_string("1100000.0000"); BOOST_REQUIRE_EQUAL( success(), rentcpu( frank, frank, rent_payment, rent_payment ) ); @@ -4388,11 +4388,11 @@ BOOST_FIXTURE_TEST_CASE( rex_lower_bound, eosio_system_tester ) try { const int64_t tot_lent = rex_pool["total_lent"].as().get_amount(); const int64_t tot_lendable = rex_pool["total_lendable"].as().get_amount(); double rex_per_eos = double(tot_rex) / double(tot_lendable); - int64_t sell_amount = rex_per_eos * ( tot_unlent - 1.9 * tot_lent / 10. ); + int64_t sell_amount = rex_per_eos * ( tot_unlent - 0.99 * tot_lent ); produce_block( fc::days(5) ); BOOST_REQUIRE_EQUAL( success(), sellrex( alice, asset( sell_amount, rex_sym ) ) ); BOOST_REQUIRE_EQUAL( success(), cancelrexorder( alice ) ); - sell_amount = rex_per_eos * ( tot_unlent - 2. * tot_lent / 10. ); + sell_amount = rex_per_eos * ( tot_unlent - tot_lent ); BOOST_REQUIRE_EQUAL( success(), sellrex( alice, asset( sell_amount, rex_sym ) ) ); BOOST_REQUIRE_EQUAL( wasm_assert_msg("no sellrex order is scheduled"), cancelrexorder( alice ) ); From c99ac02de1f847bb9a7af102dbca831c09d7f24f Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Wed, 30 Jan 2019 19:06:31 -0500 Subject: [PATCH 14/23] Small change --- contracts/eosio.system/src/rex.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index b1450eeb6..0e952057d 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -4,7 +4,6 @@ #include -#include namespace eosiosystem { From a3b6ce3c6bc271f740f37c984f4456adb6f21969 Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Thu, 31 Jan 2019 11:17:36 -0500 Subject: [PATCH 15/23] Small comment changes --- contracts/eosio.system/src/rex.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index 0e952057d..d0bb014c4 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -4,11 +4,10 @@ #include - namespace eosiosystem { /** - * @brief Deposits SYS tokens to user REX fund + * @brief Deposits core tokens to user REX fund * * @param owner - REX fund owner * @param amount - amount of tokens to be deposited @@ -26,7 +25,7 @@ namespace eosiosystem { } /** - * @brief Withdraws SYS tokens from user REX fund + * @brief Withdraws core tokens from user REX fund * * @param owner - REX fund owner * @param amount - amount of tokens to be withdrawn @@ -44,10 +43,10 @@ namespace eosiosystem { } /** - * @brief Buys REX in exchange for SYS tokens taken out of user REX fund + * @brief Buys REX in exchange for core tokens taken out of user REX fund * * @param from - owner account name - * @param amount - amount of SYS tokens to be used for purchase + * @param amount - amount of core tokens to be used for purchase */ void system_contract::buyrex( const name& from, const asset& amount ) { @@ -66,7 +65,7 @@ namespace eosiosystem { } /** - * @brief Buys REX using staked SYS tokens + * @brief Buys REX using staked core tokens * * @param owner - owner of staked tokens account name * @param receiver - account name that tokens have previously been staked to @@ -110,7 +109,7 @@ namespace eosiosystem { } /** - * @brief Sells REX in exchange for SYS tokens + * @brief Sells REX in exchange for core tokens * * @param from - owner of REX tokens * @param rex - amount of REX tokens to be sold @@ -173,8 +172,8 @@ namespace eosiosystem { } /** - * Rents as many SYS tokens as determined by market price and stakes them for CPU bandwidth - * for the benefit of receiver account. After 30 days the rented SYS delegation of CPU will + * Rents as many core tokens as determined by market price and stakes them for CPU bandwidth + * for the benefit of receiver account. After 30 days the rented core delegation of CPU will * expire or be renewed at new market price depending on available loan fund. * * @brief Rents CPU resources for 30 days in exchange for market-determined price @@ -194,8 +193,8 @@ namespace eosiosystem { } /** - * Rents as many SYS tokens as determined by market price and stakes them for NET bandwidth - * for the benefit of receiver account. After 30 days the rented SYS delegation of NET will + * Rents as many core tokens as determined by market price and stakes them for NET bandwidth + * for the benefit of receiver account. After 30 days the rented core delegation of NET will * expire or be renewed at new market price depending on available loan fund. * * @brief Rents NET resources for 30 days in exchange for market-determined price From 8dc8e1c8c42e2375825d8fe5ac74a7bdcefcf7d8 Mon Sep 17 00:00:00 2001 From: arhag Date: Thu, 31 Jan 2019 17:27:55 -0500 Subject: [PATCH 16/23] fix setabi_bios test The tester instance should be configured to not push its bios contract at startup since the unit test is going to push its own bios contract. Previously, if both bios contracts happened to be identical, the test would fail due to a duplicate transaction. --- tests/eosio.system_tests.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/eosio.system_tests.cpp b/tests/eosio.system_tests.cpp index 4740cebc6..c5ecd27b2 100644 --- a/tests/eosio.system_tests.cpp +++ b/tests/eosio.system_tests.cpp @@ -4321,29 +4321,30 @@ BOOST_FIXTURE_TEST_CASE( close_rex, eosio_system_tester ) try { } FC_LOG_AND_RETHROW() -BOOST_FIXTURE_TEST_CASE( setabi_bios, TESTER ) try { - abi_serializer abi_ser(fc::json::from_string( (const char*)contracts::system_abi().data()).template as(), abi_serializer_max_time); - set_code( config::system_account_name, contracts::bios_wasm() ); - set_abi( config::system_account_name, contracts::bios_abi().data() ); - create_account(N(eosio.token)); - set_abi( N(eosio.token), contracts::token_abi().data() ); +BOOST_AUTO_TEST_CASE( setabi_bios ) try { + validating_tester t( validating_tester::default_config() ); + abi_serializer abi_ser(fc::json::from_string( (const char*)contracts::bios_abi().data()).template as(), base_tester::abi_serializer_max_time); + t.set_code( config::system_account_name, contracts::bios_wasm() ); + t.set_abi( config::system_account_name, contracts::bios_abi().data() ); + t.create_account(N(eosio.token)); + t.set_abi( N(eosio.token), contracts::token_abi().data() ); { - auto res = get_row_by_account( config::system_account_name, config::system_account_name, N(abihash), N(eosio.token) ); + auto res = t.get_row_by_account( config::system_account_name, config::system_account_name, N(abihash), N(eosio.token) ); _abi_hash abi_hash; - auto abi_hash_var = abi_ser.binary_to_variant( "abi_hash", res, abi_serializer_max_time ); - abi_serializer::from_variant( abi_hash_var, abi_hash, get_resolver(), abi_serializer_max_time); + auto abi_hash_var = abi_ser.binary_to_variant( "abi_hash", res, base_tester::abi_serializer_max_time ); + abi_serializer::from_variant( abi_hash_var, abi_hash, t.get_resolver(), base_tester::abi_serializer_max_time); auto abi = fc::raw::pack(fc::json::from_string( (const char*)contracts::token_abi().data()).template as()); auto result = fc::sha256::hash( (const char*)abi.data(), abi.size() ); BOOST_REQUIRE( abi_hash.hash == result ); } - set_abi( N(eosio.token), contracts::system_abi().data() ); + t.set_abi( N(eosio.token), contracts::system_abi().data() ); { - auto res = get_row_by_account( config::system_account_name, config::system_account_name, N(abihash), N(eosio.token) ); + auto res = t.get_row_by_account( config::system_account_name, config::system_account_name, N(abihash), N(eosio.token) ); _abi_hash abi_hash; - auto abi_hash_var = abi_ser.binary_to_variant( "abi_hash", res, abi_serializer_max_time ); - abi_serializer::from_variant( abi_hash_var, abi_hash, get_resolver(), abi_serializer_max_time); + auto abi_hash_var = abi_ser.binary_to_variant( "abi_hash", res, base_tester::abi_serializer_max_time ); + abi_serializer::from_variant( abi_hash_var, abi_hash, t.get_resolver(), base_tester::abi_serializer_max_time); auto abi = fc::raw::pack(fc::json::from_string( (const char*)contracts::system_abi().data()).template as()); auto result = fc::sha256::hash( (const char*)abi.data(), abi.size() ); From 5a2e3b0e4f81934034e4c18c6fee82b281b85db4 Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Mon, 4 Feb 2019 19:52:59 -0500 Subject: [PATCH 17/23] Create REX savings --- .../include/eosio.system/eosio.system.hpp | 14 +++ contracts/eosio.system/src/rex.cpp | 102 +++++++++++++++++- 2 files changed, 114 insertions(+), 2 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 066aeeb60..fee8021eb 100755 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -467,6 +467,18 @@ namespace eosiosystem { [[eosio::action]] void consolidate( const name& owner ); + /** + * + */ + [[eosio::action]] + void mvtosavings( const name& owner, const asset& rex ); + + /** + * + */ + [[eosio::action]] + void mvfrsavings( const name& owner, const asset& rex ); + /** * Deletes owner records from REX tables and frees used RAM. * Owner must not have an outstanding REX balance. @@ -604,6 +616,8 @@ namespace eosiosystem { void process_rex_maturities( const rex_balance_table::const_iterator& bitr ); void consolidate_rex_balance( const rex_balance_table::const_iterator& bitr, const asset& rex_in_sell_order ); + int64_t read_rex_savings( const rex_balance_table::const_iterator& bitr ); + void put_rex_savings( const rex_balance_table::const_iterator& bitr, int64_t rex ); void update_rex_stake( const name& voter ); // defined in delegate_bandwidth.cpp diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index d0bb014c4..c5a387075 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -337,6 +337,69 @@ namespace eosiosystem { consolidate_rex_balance( bitr, rex_in_sell_order ); } + /** + * + */ + void system_contract::mvtosavings( const name& owner, const asset& rex ) + { + require_auth( owner ); + + runrex(2); + auto bitr = _rexbalance.require_find( owner.value, "account has no REX balance" ); + check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, "asset must be a positive amount of (REX, 4)" ); + asset rex_in_sell_order = update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); + check( rex + rex_in_sell_order <= bitr->rex_balance, "insufficient REX balance" ); + process_rex_maturities( bitr ); + int64_t rex_in_savings = read_rex_savings( bitr ); + int64_t moved_rex = 0; + _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { + int64_t moved_rex = 0; + while ( moved_rex < rex.amount ) { + if ( !rb.rex_maturities.empty() ) { + int64_t drex = std::min( rex.amount - moved_rex, rb.rex_maturities.back().second ); + rb.rex_maturities.back().second -= drex; + moved_rex += drex; + if ( rb.rex_maturities.back().second == 0 ) { + rb.rex_maturities.pop_back(); + } + } else { + int64_t drex = rex.amount - moved_rex; + rb.matured_rex -= drex; + moved_rex += drex; + check( rex_in_sell_order.amount <= rb.matured_rex, " " ); + } + } + }); + put_rex_savings( bitr, rex_in_savings + moved_rex ); + } + + /** + * + */ + void system_contract::mvfrsavings( const name& owner, const asset& rex ) + { + require_auth( owner ); + + runrex(2); + auto bitr = _rexbalance.require_find( owner.value, "account has no REX balance" ); + check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, "asset must be a positive amount of (REX, 4)" ); + const static time_point_sec end_of_days{ std::numeric_limits::max() }; + const auto& maturities = bitr->rex_maturities; + check( !maturities.empty() && maturities.back().first == end_of_days && rex.amount < maturities.back().second, + "insufficient REX in savings" ); + const int64_t rex_in_savings = read_rex_savings( bitr ); + _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { + rb.rex_maturities.pop_back(); + const time_point_sec maturity = get_rex_maturity(); + if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == maturity ) { + rb.rex_maturities.back().second += rex.amount; + } else { + rb.rex_maturities.emplace_back( maturity, rex.amount ); + } + }); + put_rex_savings( bitr, rex_in_savings - rex.amount ); + } + /** * @brief Deletes unused REX-related database entries and frees RAM * @@ -821,7 +884,7 @@ namespace eosiosystem { { const uint32_t num_of_maturity_buckets = 5; static const uint32_t now = current_time_point_sec().utc_seconds; - static const uint32_t r = now % seconds_per_day; + static const uint32_t r = now % seconds_per_day; static const time_point_sec rms{ now - r + num_of_maturity_buckets * seconds_per_day }; return rms; } @@ -851,6 +914,7 @@ namespace eosiosystem { void system_contract::consolidate_rex_balance( const rex_balance_table::const_iterator& bitr, const asset& rex_in_sell_order ) { + const int64_t rex_in_savings = read_rex_savings( bitr ); _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { int64_t total = rb.matured_rex - rex_in_sell_order.amount; rb.matured_rex = rex_in_sell_order.amount; @@ -862,6 +926,7 @@ namespace eosiosystem { rb.rex_maturities.emplace_back( get_rex_maturity(), total ); } }); + put_rex_savings( bitr, rex_in_savings ); } /** @@ -956,6 +1021,7 @@ namespace eosiosystem { } process_rex_maturities( bitr ); + const int64_t rex_in_savings = read_rex_savings( bitr ); const time_point_sec maturity = get_rex_maturity(); _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == maturity ) { @@ -964,10 +1030,42 @@ namespace eosiosystem { rb.rex_maturities.emplace_back( maturity, rex_received.amount ); } }); - + put_rex_savings( bitr, rex_in_savings ); return current_rex_stake - init_rex_stake; } + /** + * + */ + int64_t system_contract::read_rex_savings( const rex_balance_table::const_iterator& bitr ) + { + int64_t rex_in_savings = 0; + static const time_point_sec end_of_days{ std::numeric_limits::max() }; + _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { + if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == end_of_days ) { + rex_in_savings = rb.rex_maturities.back().second; + rb.rex_maturities.pop_back(); + } + }); + return rex_in_savings; + } + + /** + * + */ + void system_contract::put_rex_savings( const rex_balance_table::const_iterator& bitr, int64_t rex ) + { + if ( rex == 0 ) return; + static const time_point_sec end_of_days{ std::numeric_limits::max() }; + _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { + if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == end_of_days ) { + rb.rex_maturities.back().second += rex; + } else { + rb.rex_maturities.emplace_back( end_of_days, rex ); + } + }); + } + /** * @brief Updates voter REX vote stake to the current value of REX tokens held * From c8010b3f56b9c6bd707e5b1dd678a2267441008d Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Mon, 4 Feb 2019 22:27:46 -0500 Subject: [PATCH 18/23] Create REX savings - 2 --- contracts/eosio.system/src/rex.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index c5a387075..16bcb3a29 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -345,13 +345,13 @@ namespace eosiosystem { require_auth( owner ); runrex(2); + auto bitr = _rexbalance.require_find( owner.value, "account has no REX balance" ); check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, "asset must be a positive amount of (REX, 4)" ); asset rex_in_sell_order = update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); check( rex + rex_in_sell_order <= bitr->rex_balance, "insufficient REX balance" ); process_rex_maturities( bitr ); - int64_t rex_in_savings = read_rex_savings( bitr ); - int64_t moved_rex = 0; + const int64_t rex_in_savings = read_rex_savings( bitr ); _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { int64_t moved_rex = 0; while ( moved_rex < rex.amount ) { @@ -366,11 +366,11 @@ namespace eosiosystem { int64_t drex = rex.amount - moved_rex; rb.matured_rex -= drex; moved_rex += drex; - check( rex_in_sell_order.amount <= rb.matured_rex, " " ); + check( rex_in_sell_order.amount <= rb.matured_rex, "logic error" ); } } }); - put_rex_savings( bitr, rex_in_savings + moved_rex ); + put_rex_savings( bitr, rex_in_savings + rex.amount ); } /** @@ -381,13 +381,11 @@ namespace eosiosystem { require_auth( owner ); runrex(2); + auto bitr = _rexbalance.require_find( owner.value, "account has no REX balance" ); check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, "asset must be a positive amount of (REX, 4)" ); - const static time_point_sec end_of_days{ std::numeric_limits::max() }; - const auto& maturities = bitr->rex_maturities; - check( !maturities.empty() && maturities.back().first == end_of_days && rex.amount < maturities.back().second, - "insufficient REX in savings" ); const int64_t rex_in_savings = read_rex_savings( bitr ); + check( rex.amount <= rex_in_savings, "insufficient REX in savings" ); _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { rb.rex_maturities.pop_back(); const time_point_sec maturity = get_rex_maturity(); @@ -398,6 +396,7 @@ namespace eosiosystem { } }); put_rex_savings( bitr, rex_in_savings - rex.amount ); + update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); } /** @@ -1022,8 +1021,8 @@ namespace eosiosystem { process_rex_maturities( bitr ); const int64_t rex_in_savings = read_rex_savings( bitr ); - const time_point_sec maturity = get_rex_maturity(); _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { + const time_point_sec maturity = get_rex_maturity(); if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == maturity ) { rb.rex_maturities.back().second += rex_received.amount; } else { From c40bac82d341d34218e606456229d3af8ed3ac71 Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Tue, 5 Feb 2019 02:59:22 -0500 Subject: [PATCH 19/23] Create REX savings - testing --- contracts/eosio.system/src/eosio.system.cpp | 2 +- contracts/eosio.system/src/rex.cpp | 30 ++-- tests/eosio.system_tester.hpp | 8 + tests/eosio.system_tests.cpp | 170 ++++++++++++++++++++ 4 files changed, 193 insertions(+), 17 deletions(-) diff --git a/contracts/eosio.system/src/eosio.system.cpp b/contracts/eosio.system/src/eosio.system.cpp index 608d818f8..4328afc69 100755 --- a/contracts/eosio.system/src/eosio.system.cpp +++ b/contracts/eosio.system/src/eosio.system.cpp @@ -467,7 +467,7 @@ EOSIO_DISPATCH( eosiosystem::system_contract, (rmvproducer)(updtrevision)(bidname)(bidrefund) // rex.cpp (deposit)(withdraw)(buyrex)(unstaketorex)(sellrex)(cnclrexorder)(rentcpu)(rentnet)(fundcpuloan)(fundnetloan) - (defcpuloan)(defnetloan)(updaterex)(consolidate)(rexexec)(closerex) + (defcpuloan)(defnetloan)(updaterex)(consolidate)(mvtosavings)(mvfrsavings)(rexexec)(closerex) // delegate_bandwidth.cpp (buyrambytes)(buyram)(sellram)(delegatebw)(undelegatebw)(refund) // voting.cpp diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index 16bcb3a29..201569ef0 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -349,26 +349,25 @@ namespace eosiosystem { auto bitr = _rexbalance.require_find( owner.value, "account has no REX balance" ); check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, "asset must be a positive amount of (REX, 4)" ); asset rex_in_sell_order = update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); - check( rex + rex_in_sell_order <= bitr->rex_balance, "insufficient REX balance" ); - process_rex_maturities( bitr ); const int64_t rex_in_savings = read_rex_savings( bitr ); + check( rex.amount + rex_in_sell_order.amount + rex_in_savings <= bitr->rex_balance.amount, "insufficient REX balance" ); + process_rex_maturities( bitr ); _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { int64_t moved_rex = 0; - while ( moved_rex < rex.amount ) { - if ( !rb.rex_maturities.empty() ) { - int64_t drex = std::min( rex.amount - moved_rex, rb.rex_maturities.back().second ); - rb.rex_maturities.back().second -= drex; - moved_rex += drex; - if ( rb.rex_maturities.back().second == 0 ) { - rb.rex_maturities.pop_back(); - } - } else { - int64_t drex = rex.amount - moved_rex; - rb.matured_rex -= drex; - moved_rex += drex; - check( rex_in_sell_order.amount <= rb.matured_rex, "logic error" ); + while ( !rb.rex_maturities.empty() && moved_rex < rex.amount) { + int64_t drex = std::min( rex.amount - moved_rex, rb.rex_maturities.back().second ); + rb.rex_maturities.back().second -= drex; + moved_rex += drex; + if ( rb.rex_maturities.back().second == 0 ) { + rb.rex_maturities.pop_back(); } } + if ( moved_rex < rex.amount ) { + int64_t drex = rex.amount - moved_rex; + rb.matured_rex -= drex; + moved_rex += drex; + check( rex_in_sell_order.amount <= rb.matured_rex, "logic error" ); + } }); put_rex_savings( bitr, rex_in_savings + rex.amount ); } @@ -387,7 +386,6 @@ namespace eosiosystem { const int64_t rex_in_savings = read_rex_savings( bitr ); check( rex.amount <= rex_in_savings, "insufficient REX in savings" ); _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { - rb.rex_maturities.pop_back(); const time_point_sec maturity = get_rex_maturity(); if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == maturity ) { rb.rex_maturities.back().second += rex.amount; diff --git a/tests/eosio.system_tester.hpp b/tests/eosio.system_tester.hpp index 1bde1f559..cadbf7d86 100644 --- a/tests/eosio.system_tester.hpp +++ b/tests/eosio.system_tester.hpp @@ -500,6 +500,14 @@ class eosio_system_tester : public TESTER { return push_action( name(owner), N(consolidate), mvo()("owner", owner) ); } + action_result mvtosavings( const account_name& owner, const asset& rex ) { + return push_action( name(owner), N(mvtosavings), mvo()("owner", owner)("rex", rex) ); + } + + action_result mvfrsavings( const account_name& owner, const asset& rex ) { + return push_action( name(owner), N(mvfrsavings), mvo()("owner", owner)("rex", rex) ); + } + action_result closerex( const account_name& owner ) { return push_action( name(owner), N(closerex), mvo()("owner", owner) ); } diff --git a/tests/eosio.system_tests.cpp b/tests/eosio.system_tests.cpp index 1acc15abd..8a505e56a 100644 --- a/tests/eosio.system_tests.cpp +++ b/tests/eosio.system_tests.cpp @@ -4192,6 +4192,176 @@ BOOST_FIXTURE_TEST_CASE( rex_maturity, eosio_system_tester ) try { } FC_LOG_AND_RETHROW() +BOOST_FIXTURE_TEST_CASE( rex_savings, eosio_system_tester ) try { + + const asset init_balance = core_sym::from_string("100000.0000"); + const std::vector accounts = { N(aliceaccount), N(bobbyaccount), N(carolaccount), N(emilyaccount), N(frankaccount) }; + account_name alice = accounts[0], bob = accounts[1], carol = accounts[2], emily = accounts[3], frank = accounts[4]; + setup_rex_accounts( accounts, init_balance ); + + const int64_t rex_ratio = 10000; + const symbol rex_sym( SY(4, REX) ); + + { + const asset payment1 = core_sym::from_string("14.8000"); + const asset payment2 = core_sym::from_string("15.2000"); + const asset payment = payment1 + payment2; + const asset rex_bucket( rex_ratio * payment.get_amount(), rex_sym ); + for ( uint8_t i = 0; i < 8; ++i ) { + BOOST_REQUIRE_EQUAL( success(), buyrex( alice, payment1 ) ); + produce_block( fc::hours(12) ); + BOOST_REQUIRE_EQUAL( success(), buyrex( alice, payment2 ) ); + produce_block( fc::hours(14) ); + } + + auto rex_balance = get_rex_balance_obj( alice ); + BOOST_REQUIRE_EQUAL( 8 * rex_bucket.get_amount(), rex_balance["rex_balance"].as().get_amount() ); + BOOST_REQUIRE_EQUAL( 5, rex_balance["rex_maturities"].get_array().size() ); + BOOST_REQUIRE_EQUAL( 4 * rex_bucket.get_amount(), rex_balance["matured_rex"].as() ); + + BOOST_REQUIRE_EQUAL( success(), mvtosavings( alice, asset( 8 * rex_bucket.get_amount(), rex_sym ) ) ); + rex_balance = get_rex_balance_obj( alice ); + BOOST_REQUIRE_EQUAL( 1, rex_balance["rex_maturities"].get_array().size() ); + BOOST_REQUIRE_EQUAL( 0, rex_balance["matured_rex"].as() ); + produce_block( fc::days(100) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient available rex"), + sellrex( alice, asset::from_string( "1.0000 REX" ) ) ); + BOOST_REQUIRE_EQUAL( success(), mvfrsavings( alice, asset::from_string( "10.0000 REX" ) ) ); + rex_balance = get_rex_balance_obj( alice ); + BOOST_REQUIRE_EQUAL( 2, rex_balance["rex_maturities"].get_array().size() ); + produce_block( fc::days(3) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient available rex"), + sellrex( alice, asset::from_string( "1.0000 REX" ) ) ); + produce_blocks( 2 ); + produce_block( fc::days(2) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient available rex"), + sellrex( alice, asset::from_string( "10.0001 REX" ) ) ); + BOOST_REQUIRE_EQUAL( success(), sellrex( alice, asset::from_string( "10.0000 REX" ) ) ); + rex_balance = get_rex_balance_obj( alice ); + BOOST_REQUIRE_EQUAL( 1, rex_balance["rex_maturities"].get_array().size() ); + produce_block( fc::days(100) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient available rex"), + sellrex( alice, asset::from_string( "0.0001 REX" ) ) ); + BOOST_REQUIRE_EQUAL( success(), mvfrsavings( alice, get_rex_balance( alice ) ) ); + produce_block( fc::days(5) ); + BOOST_REQUIRE_EQUAL( success(), sellrex( alice, get_rex_balance( alice ) ) ); + } + + { + const asset payment = core_sym::from_string("20.0000"); + const asset rex_bucket( rex_ratio * payment.get_amount(), rex_sym ); + for ( uint8_t i = 0; i < 5; ++i ) { + produce_block( fc::hours(24) ); + BOOST_REQUIRE_EQUAL( success(), buyrex( bob, payment ) ); + } + + auto rex_balance = get_rex_balance_obj( bob ); + BOOST_REQUIRE_EQUAL( 5 * rex_bucket.get_amount(), rex_balance["rex_balance"].as().get_amount() ); + BOOST_REQUIRE_EQUAL( 5, rex_balance["rex_maturities"].get_array().size() ); + BOOST_REQUIRE_EQUAL( 0, rex_balance["matured_rex"].as() ); + BOOST_REQUIRE_EQUAL( success(), mvtosavings( bob, asset( rex_bucket.get_amount() / 2, rex_sym ) ) ); + rex_balance = get_rex_balance_obj( bob ); + BOOST_REQUIRE_EQUAL( 6, rex_balance["rex_maturities"].get_array().size() ); + + BOOST_REQUIRE_EQUAL( success(), mvtosavings( bob, asset( rex_bucket.get_amount() / 2, rex_sym ) ) ); + rex_balance = get_rex_balance_obj( bob ); + BOOST_REQUIRE_EQUAL( 5, rex_balance["rex_maturities"].get_array().size() ); + produce_block( fc::days(1) ); + BOOST_REQUIRE_EQUAL( success(), sellrex( bob, rex_bucket ) ); + rex_balance = get_rex_balance_obj( bob ); + BOOST_REQUIRE_EQUAL( 4, rex_balance["rex_maturities"].get_array().size() ); + BOOST_REQUIRE_EQUAL( 0, rex_balance["matured_rex"].as() ); + BOOST_REQUIRE_EQUAL( 4 * rex_bucket.get_amount(), rex_balance["rex_balance"].as().get_amount() ); + + BOOST_REQUIRE_EQUAL( success(), mvtosavings( bob, asset( 3 * rex_bucket.get_amount() / 2, rex_sym ) ) ); + rex_balance = get_rex_balance_obj( bob ); + BOOST_REQUIRE_EQUAL( 3, rex_balance["rex_maturities"].get_array().size() ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient available rex"), + sellrex( bob, rex_bucket ) ); + + produce_block( fc::days(1) ); + BOOST_REQUIRE_EQUAL( success(), sellrex( bob, rex_bucket ) ); + rex_balance = get_rex_balance_obj( bob ); + BOOST_REQUIRE_EQUAL( 2, rex_balance["rex_maturities"].get_array().size() ); + BOOST_REQUIRE_EQUAL( 0, rex_balance["matured_rex"].as() ); + BOOST_REQUIRE_EQUAL( 3 * rex_bucket.get_amount(), rex_balance["rex_balance"].as().get_amount() ); + + produce_block( fc::days(1) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient available rex"), + sellrex( bob, rex_bucket ) ); + BOOST_REQUIRE_EQUAL( success(), sellrex( bob, asset( rex_bucket.get_amount() / 2, rex_sym ) ) ); + rex_balance = get_rex_balance_obj( bob ); + BOOST_REQUIRE_EQUAL( 1, rex_balance["rex_maturities"].get_array().size() ); + BOOST_REQUIRE_EQUAL( 0, rex_balance["matured_rex"].as() ); + BOOST_REQUIRE_EQUAL( 5 * rex_bucket.get_amount(), 2 * rex_balance["rex_balance"].as().get_amount() ); + + produce_block( fc::days(20) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient available rex"), + sellrex( bob, rex_bucket ) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient REX in savings"), + mvfrsavings( bob, asset( 3 * rex_bucket.get_amount(), rex_sym ) ) ); + BOOST_REQUIRE_EQUAL( success(), mvfrsavings( bob, rex_bucket ) ); + BOOST_REQUIRE_EQUAL( 2, get_rex_balance_obj( bob )["rex_maturities"].get_array().size() ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient REX balance"), + mvtosavings( bob, asset( 3 * rex_bucket.get_amount() / 2, rex_sym ) ) ); + produce_block( fc::days(1) ); + BOOST_REQUIRE_EQUAL( success(), mvfrsavings( bob, rex_bucket ) ); + BOOST_REQUIRE_EQUAL( 3, get_rex_balance_obj( bob )["rex_maturities"].get_array().size() ); + produce_block( fc::days(4) ); + BOOST_REQUIRE_EQUAL( success(), sellrex( bob, rex_bucket ) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient available rex"), + sellrex( bob, rex_bucket ) ); + produce_block( fc::days(1) ); + BOOST_REQUIRE_EQUAL( success(), sellrex( bob, rex_bucket ) ); + rex_balance = get_rex_balance_obj( bob ); + BOOST_REQUIRE_EQUAL( 1, rex_balance["rex_maturities"].get_array().size() ); + BOOST_REQUIRE_EQUAL( rex_bucket.get_amount() / 2, rex_balance["rex_balance"].as().get_amount() ); + + BOOST_REQUIRE_EQUAL( success(), mvfrsavings( bob, asset( rex_bucket.get_amount() / 4, rex_sym ) ) ); + produce_block( fc::days(2) ); + BOOST_REQUIRE_EQUAL( success(), mvfrsavings( bob, asset( rex_bucket.get_amount() / 8, rex_sym ) ) ); + BOOST_REQUIRE_EQUAL( 3, get_rex_balance_obj( bob )["rex_maturities"].get_array().size() ); + BOOST_REQUIRE_EQUAL( success(), consolidate( bob ) ); + BOOST_REQUIRE_EQUAL( 2, get_rex_balance_obj( bob )["rex_maturities"].get_array().size() ); + + produce_block( fc::days(5) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient available rex"), + sellrex( bob, asset( rex_bucket.get_amount() / 2, rex_sym ) ) ); + BOOST_REQUIRE_EQUAL( success(), sellrex( bob, asset( 3 * rex_bucket.get_amount() / 8, rex_sym ) ) ); + rex_balance = get_rex_balance_obj( bob ); + BOOST_REQUIRE_EQUAL( 1, rex_balance["rex_maturities"].get_array().size() ); + BOOST_REQUIRE_EQUAL( 0, rex_balance["matured_rex"].as() ); + BOOST_REQUIRE_EQUAL( rex_bucket.get_amount() / 8, rex_balance["rex_balance"].as().get_amount() ); + BOOST_REQUIRE_EQUAL( success(), mvfrsavings( bob, get_rex_balance( bob ) ) ); + produce_block( fc::days(5) ); + BOOST_REQUIRE_EQUAL( success(), sellrex( bob, get_rex_balance( bob ) ) ); + } + + { + const asset payment = core_sym::from_string("20000.0000"); + const int64_t rex_bucket_amount = rex_ratio * payment.get_amount(); + const asset rex_bucket( rex_bucket_amount, rex_sym ); + BOOST_REQUIRE_EQUAL( success(), buyrex( alice, payment ) ); + BOOST_REQUIRE_EQUAL( rex_bucket, get_rex_balance( alice ) ); + BOOST_REQUIRE_EQUAL( rex_bucket, get_rex_pool()["total_rex"].as() ); + + produce_block( fc::days(5) ); + + BOOST_REQUIRE_EQUAL( success(), rentcpu( bob, bob, core_sym::from_string("2000.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), sellrex( alice, asset( 9 * rex_bucket_amount / 10, rex_sym ) ) ); + BOOST_REQUIRE_EQUAL( rex_bucket, get_rex_balance( alice ) ); + BOOST_REQUIRE_EQUAL( success(), mvtosavings( alice, asset( rex_bucket_amount / 10, rex_sym ) ) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient REX balance"), + mvtosavings( alice, asset( rex_bucket_amount / 10, rex_sym ) ) ); + BOOST_REQUIRE_EQUAL( success(), cancelrexorder( alice ) ); + BOOST_REQUIRE_EQUAL( success(), mvtosavings( alice, asset( rex_bucket_amount / 10, rex_sym ) ) ); + auto rb = get_rex_balance_obj( alice ); + BOOST_REQUIRE_EQUAL( rb["matured_rex"].as(), 8 * rex_bucket_amount / 10 ); + } + +} FC_LOG_AND_RETHROW() + + BOOST_FIXTURE_TEST_CASE( update_rex, eosio_system_tester, * boost::unit_test::tolerance(1e-10) ) try { const asset init_balance = core_sym::from_string("10000.0000"); From a4b880820cb7244cbe4503aee653dbed1efe1f6d Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Tue, 5 Feb 2019 14:00:57 -0500 Subject: [PATCH 20/23] Comments, small changes --- .../include/eosio.system/eosio.system.hpp | 8 ++++-- contracts/eosio.system/src/rex.cpp | 26 ++++++++++++------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index fee8021eb..5d452767c 100755 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -468,13 +468,17 @@ namespace eosiosystem { void consolidate( const name& owner ); /** - * + * Moves a specified amount of REX into savings bucket. REX savings bucket + * never matures. In order for it to be sold, it has to be moved explicitly + * out of that bucket. Then the moved amount will have the regular maturity + * period of 4 days starting from the end of the day. */ [[eosio::action]] void mvtosavings( const name& owner, const asset& rex ); /** - * + * Moves a specified amount of REX out of savings bucket. The moved amount + * will have the regular REX maturity period of 4 days. */ [[eosio::action]] void mvfrsavings( const name& owner, const asset& rex ); diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index 201569ef0..e3399d0bd 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -338,7 +338,10 @@ namespace eosiosystem { } /** + * @brief Moves a specified amount of REX to savings bucket * + * @param owner - account name of REX owner + * @param rex - amount of REX to be moved */ void system_contract::mvtosavings( const name& owner, const asset& rex ) { @@ -348,14 +351,15 @@ namespace eosiosystem { auto bitr = _rexbalance.require_find( owner.value, "account has no REX balance" ); check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, "asset must be a positive amount of (REX, 4)" ); - asset rex_in_sell_order = update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); - const int64_t rex_in_savings = read_rex_savings( bitr ); - check( rex.amount + rex_in_sell_order.amount + rex_in_savings <= bitr->rex_balance.amount, "insufficient REX balance" ); + const asset rex_in_sell_order = update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); + const int64_t rex_in_savings = read_rex_savings( bitr ); + check( rex.amount + rex_in_sell_order.amount + rex_in_savings <= bitr->rex_balance.amount, + "insufficient REX balance" ); process_rex_maturities( bitr ); _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { int64_t moved_rex = 0; while ( !rb.rex_maturities.empty() && moved_rex < rex.amount) { - int64_t drex = std::min( rex.amount - moved_rex, rb.rex_maturities.back().second ); + const int64_t drex = std::min( rex.amount - moved_rex, rb.rex_maturities.back().second ); rb.rex_maturities.back().second -= drex; moved_rex += drex; if ( rb.rex_maturities.back().second == 0 ) { @@ -363,17 +367,21 @@ namespace eosiosystem { } } if ( moved_rex < rex.amount ) { - int64_t drex = rex.amount - moved_rex; - rb.matured_rex -= drex; - moved_rex += drex; - check( rex_in_sell_order.amount <= rb.matured_rex, "logic error" ); + const int64_t drex = rex.amount - moved_rex; + rb.matured_rex -= drex; + moved_rex += drex; + check( rex_in_sell_order.amount <= rb.matured_rex, "logic error in mvtosavings" ); } + check( moved_rex == rex.amount, "programmer error in mvtosavings" ); }); put_rex_savings( bitr, rex_in_savings + rex.amount ); } /** + * @brief Moves a specified amount of REX from savings bucket * + * @param owner - account name of REX owner + * @param rex - amount of REX to be moved */ void system_contract::mvfrsavings( const name& owner, const asset& rex ) { @@ -1018,7 +1026,7 @@ namespace eosiosystem { } process_rex_maturities( bitr ); - const int64_t rex_in_savings = read_rex_savings( bitr ); + const int64_t rex_in_savings = read_rex_savings( bitr ); _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { const time_point_sec maturity = get_rex_maturity(); if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == maturity ) { From 04b43db68d36e0346687134b888fbc1de5c3cd77 Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Tue, 5 Feb 2019 17:46:44 -0500 Subject: [PATCH 21/23] Fixed bug found by @larryk85 code review, more testing --- contracts/eosio.system/src/rex.cpp | 16 ++++++++-- tests/eosio.system_tests.cpp | 49 +++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index e3399d0bd..aa8590f13 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -393,6 +393,7 @@ namespace eosiosystem { check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, "asset must be a positive amount of (REX, 4)" ); const int64_t rex_in_savings = read_rex_savings( bitr ); check( rex.amount <= rex_in_savings, "insufficient REX in savings" ); + process_rex_maturities( bitr ); _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { const time_point_sec maturity = get_rex_maturity(); if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == maturity ) { @@ -1040,12 +1041,20 @@ namespace eosiosystem { } /** + * @brief Reads amount of REX in savings bucket and removes the bucket from maturities + * + * Reads and (temporarily) removes REX savings bucket from REX maturities in order to + * allow uniform processing of remaining buckets as savings is a special case. This + * function is used in conjunction with put_rex_savings. * + * @param bitr - iterator pointing to rex_balance object + * + * @return int64_t - amount of REX in savings bucket */ int64_t system_contract::read_rex_savings( const rex_balance_table::const_iterator& bitr ) { int64_t rex_in_savings = 0; - static const time_point_sec end_of_days{ std::numeric_limits::max() }; + static const time_point_sec end_of_days = time_point_sec::maximum(); _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == end_of_days ) { rex_in_savings = rb.rex_maturities.back().second; @@ -1056,12 +1065,15 @@ namespace eosiosystem { } /** + * @brief Adds a specified REX amount to savings bucket * + * @param bitr - iterator pointing to rex_balance object + * @param rex - amount of REX to be added */ void system_contract::put_rex_savings( const rex_balance_table::const_iterator& bitr, int64_t rex ) { if ( rex == 0 ) return; - static const time_point_sec end_of_days{ std::numeric_limits::max() }; + static const time_point_sec end_of_days = time_point_sec::maximum(); _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == end_of_days ) { rb.rex_maturities.back().second += rex; diff --git a/tests/eosio.system_tests.cpp b/tests/eosio.system_tests.cpp index 8a505e56a..2404df530 100644 --- a/tests/eosio.system_tests.cpp +++ b/tests/eosio.system_tests.cpp @@ -4223,7 +4223,7 @@ BOOST_FIXTURE_TEST_CASE( rex_savings, eosio_system_tester ) try { rex_balance = get_rex_balance_obj( alice ); BOOST_REQUIRE_EQUAL( 1, rex_balance["rex_maturities"].get_array().size() ); BOOST_REQUIRE_EQUAL( 0, rex_balance["matured_rex"].as() ); - produce_block( fc::days(100) ); + produce_block( fc::days(1000) ); BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient available rex"), sellrex( alice, asset::from_string( "1.0000 REX" ) ) ); BOOST_REQUIRE_EQUAL( success(), mvfrsavings( alice, asset::from_string( "10.0000 REX" ) ) ); @@ -4357,6 +4357,53 @@ BOOST_FIXTURE_TEST_CASE( rex_savings, eosio_system_tester ) try { BOOST_REQUIRE_EQUAL( success(), mvtosavings( alice, asset( rex_bucket_amount / 10, rex_sym ) ) ); auto rb = get_rex_balance_obj( alice ); BOOST_REQUIRE_EQUAL( rb["matured_rex"].as(), 8 * rex_bucket_amount / 10 ); + BOOST_REQUIRE_EQUAL( success(), mvfrsavings( alice, asset( 2 * rex_bucket_amount / 10, rex_sym ) ) ); + produce_block( fc::days(31) ); + BOOST_REQUIRE_EQUAL( success(), sellrex( alice, get_rex_balance( alice ) ) ); + } + + { + const asset payment = core_sym::from_string("250.0000"); + const asset half_payment = core_sym::from_string("125.0000"); + const int64_t rex_bucket_amount = rex_ratio * payment.get_amount(); + const int64_t half_rex_bucket_amount = rex_bucket_amount / 2; + const asset rex_bucket( rex_bucket_amount, rex_sym ); + const asset half_rex_bucket( half_rex_bucket_amount, rex_sym ); + + BOOST_REQUIRE_EQUAL( success(), buyrex( carol, payment ) ); + BOOST_REQUIRE_EQUAL( rex_bucket, get_rex_balance( carol ) ); + auto rex_balance = get_rex_balance_obj( carol ); + + BOOST_REQUIRE_EQUAL( 1, rex_balance["rex_maturities"].get_array().size() ); + BOOST_REQUIRE_EQUAL( 0, rex_balance["matured_rex"].as() ); + produce_block( fc::days(1) ); + BOOST_REQUIRE_EQUAL( success(), buyrex( carol, payment ) ); + rex_balance = get_rex_balance_obj( carol ); + BOOST_REQUIRE_EQUAL( 2, rex_balance["rex_maturities"].get_array().size() ); + BOOST_REQUIRE_EQUAL( 0, rex_balance["matured_rex"].as() ); + + BOOST_REQUIRE_EQUAL( success(), mvtosavings( carol, half_rex_bucket ) ); + rex_balance = get_rex_balance_obj( carol ); + BOOST_REQUIRE_EQUAL( 3, rex_balance["rex_maturities"].get_array().size() ); + + BOOST_REQUIRE_EQUAL( success(), buyrex( carol, half_payment ) ); + rex_balance = get_rex_balance_obj( carol ); + BOOST_REQUIRE_EQUAL( 3, rex_balance["rex_maturities"].get_array().size() ); + + produce_block( fc::days(5) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("asset must be a positive amount of (REX, 4)"), + mvfrsavings( carol, asset::from_string("0.0000 REX") ) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("asset must be a positive amount of (REX, 4)"), + mvfrsavings( carol, asset::from_string("1.0000 RND") ) ); + BOOST_REQUIRE_EQUAL( success(), mvfrsavings( carol, half_rex_bucket ) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient REX in savings"), + mvfrsavings( carol, asset::from_string("0.0001 REX") ) ); + rex_balance = get_rex_balance_obj( carol ); + BOOST_REQUIRE_EQUAL( 1, rex_balance["rex_maturities"].get_array().size() ); + BOOST_REQUIRE_EQUAL( 5 * half_rex_bucket_amount, rex_balance["rex_balance"].as().get_amount() ); + BOOST_REQUIRE_EQUAL( 2 * rex_bucket_amount, rex_balance["matured_rex"].as() ); + produce_block( fc::days(5) ); + BOOST_REQUIRE_EQUAL( success(), sellrex( carol, get_rex_balance( carol) ) ); } } FC_LOG_AND_RETHROW() From e24442d88eee7944bd385c9964d967833cccf365 Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Wed, 6 Feb 2019 10:56:12 -0500 Subject: [PATCH 22/23] Small optimization --- contracts/eosio.system/src/rex.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index aa8590f13..70896063d 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -902,7 +902,7 @@ namespace eosiosystem { */ void system_contract::process_rex_maturities( const rex_balance_table::const_iterator& bitr ) { - time_point_sec now = current_time_point_sec(); + const time_point_sec now = current_time_point_sec(); _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { while ( !rb.rex_maturities.empty() && rb.rex_maturities.front().first <= now ) { rb.matured_rex += rb.rex_maturities.front().second; @@ -1055,12 +1055,12 @@ namespace eosiosystem { { int64_t rex_in_savings = 0; static const time_point_sec end_of_days = time_point_sec::maximum(); - _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { - if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == end_of_days ) { + if ( !bitr->rex_maturities.empty() && bitr->rex_maturities.back().first == end_of_days ) { + _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { rex_in_savings = rb.rex_maturities.back().second; rb.rex_maturities.pop_back(); - } - }); + }); + } return rex_in_savings; } From c9648b8feb4a52f1e7e81cd6a1d75835c8eebbb9 Mon Sep 17 00:00:00 2001 From: Khaled Al-Hassanieh Date: Wed, 6 Feb 2019 11:32:35 -0500 Subject: [PATCH 23/23] Small change --- contracts/eosio.system/src/rex.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index 70896063d..7a98b74f7 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -1026,8 +1026,8 @@ namespace eosiosystem { current_rex_stake.amount = bitr->vote_stake.amount; } - process_rex_maturities( bitr ); const int64_t rex_in_savings = read_rex_savings( bitr ); + process_rex_maturities( bitr ); _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { const time_point_sec maturity = get_rex_maturity(); if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == maturity ) {