diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 60cbb66909ca60b706148fa47b01b6a3482949f1..d741ace2de121380e93df79b13b4ccb94a8362e9 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -155,7 +155,7 @@ namespace eosio { namespace chain { auto itr = producer_to_last_produced.find(h.producer); if( itr != producer_to_last_produced.end() ) { - FC_ASSERT( itr->second <= result.block_num - h.confirmed, "producer double-confirming known range" ); + FC_ASSERT( itr->second < result.block_num - h.confirmed, "producer double-confirming known range" ); } // FC_ASSERT( result.header.block_mroot == h.block_mroot, "mismatch block merkle root" ); diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index c5a49ec3f7e5b0ea1712dbbd710714b4336dd0df..9f580f463ac2219dc2ff30027cede7fe46f45144 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -160,7 +160,7 @@ namespace eosio { namespace testing { auto last_produced_block_num = control->last_irreversible_block_num(); auto itr = last_produced_block.find(producer.producer_name); if (itr != last_produced_block.end()) { - last_produced_block_num = block_header::num_from_id(itr->second); + last_produced_block_num = std::max(control->last_irreversible_block_num(), block_header::num_from_id(itr->second)); } control->abort_block(); diff --git a/unittests/eosio.system_tests.cpp b/unittests/eosio.system_tests.cpp index 7ffd64b37cffc59747f929343584bd8eb4a1597d..217032e847b22888886910153e939f5ce6a2f59c 100644 --- a/unittests/eosio.system_tests.cpp +++ b/unittests/eosio.system_tests.cpp @@ -46,13 +46,27 @@ public: set_code( N(eosio.token), eosio_token_wast ); set_abi( N(eosio.token), eosio_token_abi ); + { + const auto& accnt = control->db().get( N(eosio.token) ); + abi_def abi; + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + token_abi_ser.set_abi(abi); + } + create_currency( N(eosio.token), config::system_account_name, asset::from_string("10000000000.0000 EOS") ); issue(config::system_account_name, "1000000000.0000 EOS"); BOOST_REQUIRE_EQUAL( asset::from_string("1000000000.0000 EOS"), get_balance( "eosio" ) ); set_code( config::system_account_name, eosio_system_wast ); set_abi( config::system_account_name, eosio_system_abi ); - + + { + const auto& accnt = control->db().get( config::system_account_name ); + abi_def abi; + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + abi_ser.set_abi(abi); + } + produce_blocks(); create_account_with_resources( N(alice1111111), N(eosio), asset::from_string("1.0000 EOS"), false ); @@ -60,26 +74,9 @@ public: create_account_with_resources( N(carol1111111), N(eosio), asset::from_string("1.0000 EOS"), false ); BOOST_REQUIRE_EQUAL( asset::from_string("1000000000.0000 EOS"), get_balance( "eosio" ) ); - - // eosio pays it self for these... - //BOOST_REQUIRE_EQUAL( asset::from_string("999999998.5000 EOS"), get_balance( "eosio" ) ); - - produce_blocks(); - - { - const auto& accnt = control->db().get( config::system_account_name ); - abi_def abi; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); - abi_ser.set_abi(abi); - } - { - const auto& accnt = control->db().get( N(eosio.token) ); - abi_def abi; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); - token_abi_ser.set_abi(abi); - } } + void create_accounts_with_resources( vector accounts, account_name creator = N(eosio) ) { for( auto a : accounts ) { create_account_with_resources( a, creator ); @@ -290,7 +287,6 @@ public: ("max_authority_depth", 6 + n) ("max_generated_transaction_count", 10 + n) ("max_ram_size", (n % 10 + 1) * 1024 * 1024) - ("percent_of_max_inflation_rate", 50 + n) ("ram_reserve_ratio", 100 + n); } @@ -310,23 +306,8 @@ public: } asset get_balance( const account_name& act ) { - //return get_currency_balance( config::system_account_name, symbol(SY(4,EOS)), act ); - //temporary code. current get_currency_balancy uses table name N(accounts) from currency.h - //generic_currency table name is N(account). - const auto& db = control->db(); - const auto* tbl = db.find(boost::make_tuple(N(eosio.token), act, N(accounts))); - share_type result = 0; - - // the balance is implied to be 0 if either the table or row does not exist - if (tbl) { - const auto *obj = db.find(boost::make_tuple(tbl->id, symbol(SY(4,EOS)).to_symbol_code())); - if (obj) { - // balance is the first field in the serialization - fc::datastream ds(obj->value.data(), obj->value.size()); - fc::raw::unpack(ds, result); - } - } - return asset( result, symbol(SY(4,EOS)) ); + vector data = get_row_by_account( N(eosio.token), act, N(accounts), symbol(SY(4,EOS)).to_symbol_code().value ); + return data.empty() ? asset(0, symbol(SY(4,EOS))) : token_abi_ser.binary_to_variant("account", data)["balance"].as(); } fc::variant get_total_stake( const account_name& act ) { @@ -1415,11 +1396,12 @@ BOOST_FIXTURE_TEST_CASE(producer_pay, eosio_system_tester, * boost::unit_test::t const double secs_per_year = 52 * 7 * 24 * 3600; const asset large_asset = asset::from_string("80.0000 EOS"); - create_account_with_resources( N(defproducera), N(eosio), asset::from_string("1.0000 EOS"), false, large_asset, large_asset ); - create_account_with_resources( N(defproducerb), N(eosio), asset::from_string("1.0000 EOS"), false, large_asset, large_asset ); + create_account_with_resources( N(defproducera), config::system_account_name, asset::from_string("1.0000 EOS"), false, large_asset, large_asset ); + create_account_with_resources( N(defproducerb), config::system_account_name, asset::from_string("1.0000 EOS"), false, large_asset, large_asset ); + create_account_with_resources( N(defproducerc), config::system_account_name, asset::from_string("1.0000 EOS"), false, large_asset, large_asset ); - create_account_with_resources( N(producvotera), N(eosio), asset::from_string("1.0000 EOS"), false, large_asset, large_asset ); - create_account_with_resources( N(producvoterb), N(eosio), asset::from_string("1.0000 EOS"), false, large_asset, large_asset ); + create_account_with_resources( N(producvotera), config::system_account_name, asset::from_string("1.0000 EOS"), false, large_asset, large_asset ); + create_account_with_resources( N(producvoterb), config::system_account_name, asset::from_string("1.0000 EOS"), false, large_asset, large_asset ); BOOST_REQUIRE_EQUAL(success(), regproducer(N(defproducera))); auto prod = get_producer_info( N(defproducera) ); @@ -1584,6 +1566,27 @@ BOOST_FIXTURE_TEST_CASE(producer_pay, eosio_system_tester, * boost::unit_test::t push_action(N(defproducerb), N(claimrewards), mvo()("owner", "defproducerb"))); } + // test stability over a year + { + regproducer(N(defproducerb)); + regproducer(N(defproducerc)); + const asset initial_supply = get_token_supply(); + const int64_t initial_savings = get_global_state()["savings"].as(); + for (uint32_t i = 0; i < 7 * 52; ++i) { + produce_block(fc::seconds(8 * 3600)); + BOOST_REQUIRE_EQUAL(success(), push_action(N(defproducerc), N(claimrewards), mvo()("owner", "defproducerc"))); + produce_block(fc::seconds(8 * 3600)); + BOOST_REQUIRE_EQUAL(success(), push_action(N(defproducerb), N(claimrewards), mvo()("owner", "defproducerb"))); + produce_block(fc::seconds(8 * 3600)); + BOOST_REQUIRE_EQUAL(success(), push_action(N(defproducera), N(claimrewards), mvo()("owner", "defproducera"))); + } + const asset supply = get_token_supply(); + const int64_t savings = get_global_state()["savings"].as(); + // Amount issued per year is very close to the 5% inflation target. Small difference (500 tokens out of 50'000'000 issued) + // is due to compounding every 8 hours in this test as opposed to theoretical continuous compounding + BOOST_REQUIRE(500 * 10000 > int64_t(double(initial_supply.amount) * double(0.05)) - (supply.amount - initial_supply.amount)); + BOOST_REQUIRE(500 * 10000 > int64_t(double(initial_supply.amount) * double(0.04)) - (savings - initial_savings)); + } } FC_LOG_AND_RETHROW() @@ -1592,13 +1595,13 @@ BOOST_FIXTURE_TEST_CASE(multiple_producer_pay, eosio_system_tester, * boost::uni const int64_t secs_per_year = 52 * 7 * 24 * 3600; const double usecs_per_year = secs_per_year * 1000000; - const double cont_rate = 4.879/100.; + const double cont_rate = 4.879/100.; const asset net = asset::from_string("80.0000 EOS"); const asset cpu = asset::from_string("80.0000 EOS"); - create_account_with_resources( N(producvotera), N(eosio), asset::from_string("1.0000 EOS"), false, net, cpu ); - create_account_with_resources( N(producvoterb), N(eosio), asset::from_string("1.0000 EOS"), false, net, cpu ); - create_account_with_resources( N(producvoterc), N(eosio), asset::from_string("1.0000 EOS"), false, net, cpu ); + create_account_with_resources( N(producvotera), config::system_account_name, asset::from_string("1.0000 EOS"), false, net, cpu ); + create_account_with_resources( N(producvoterb), config::system_account_name, asset::from_string("1.0000 EOS"), false, net, cpu ); + create_account_with_resources( N(producvoterc), config::system_account_name, asset::from_string("1.0000 EOS"), false, net, cpu ); // create accounts {defproducera, defproducerb, ..., defproducerz} and register as producers std::vector producer_names; @@ -1616,7 +1619,6 @@ BOOST_FIXTURE_TEST_CASE(multiple_producer_pay, eosio_system_tester, * boost::uni wdump((p)); BOOST_TEST(0 == get_producer_info(p)["total_votes"].as()); } - } { @@ -1700,6 +1702,7 @@ BOOST_FIXTURE_TEST_CASE(multiple_producer_pay, eosio_system_tester, * boost::uni vote_shares[i] = get_producer_info(producer_names[i])["total_votes"].as(); total_votes += vote_shares[i]; } + BOOST_TEST(total_votes == get_global_state()["total_producer_vote_weight"].as()); std::for_each(vote_shares.begin(), vote_shares.end(), [total_votes](double& x) { x /= total_votes; }); BOOST_TEST(double(1) == std::accumulate(vote_shares.begin(), vote_shares.end(), double(0))); @@ -1720,7 +1723,6 @@ BOOST_FIXTURE_TEST_CASE(multiple_producer_pay, eosio_system_tester, * boost::uni const uint32_t initial_tot_unpaid_blocks = initial_global_state["total_unpaid_blocks"].as(); const asset initial_supply = get_token_supply(); const asset initial_balance = get_balance(prod_name); - const uint32_t initial_unpaid_blocks = get_producer_info(prod_name)["unpaid_blocks"].as(); BOOST_REQUIRE_EQUAL(success(), push_action(prod_name, N(claimrewards), mvo()("owner", prod_name))); @@ -1733,10 +1735,7 @@ BOOST_FIXTURE_TEST_CASE(multiple_producer_pay, eosio_system_tester, * boost::uni const uint32_t tot_unpaid_blocks = global_state["total_unpaid_blocks"].as(); const asset supply = get_token_supply(); const asset balance = get_balance(prod_name); - const uint32_t unpaid_blocks = get_producer_info(prod_name)["unpaid_blocks"].as(); - - const uint64_t usecs_between_fills = claim_time - initial_claim_time; const int32_t secs_between_fills = static_cast(usecs_between_fills / 1000000); @@ -1795,7 +1794,6 @@ BOOST_FIXTURE_TEST_CASE(multiple_producer_pay, eosio_system_tester, * boost::uni const uint32_t initial_tot_unpaid_blocks = initial_global_state["total_unpaid_blocks"].as(); const asset initial_supply = get_token_supply(); const asset initial_balance = get_balance(prod_name); - const uint32_t initial_unpaid_blocks = get_producer_info(prod_name)["unpaid_blocks"].as(); BOOST_REQUIRE_EQUAL(success(), push_action(prod_name, N(claimrewards), mvo()("owner", prod_name))); @@ -1808,19 +1806,18 @@ BOOST_FIXTURE_TEST_CASE(multiple_producer_pay, eosio_system_tester, * boost::uni const uint32_t tot_unpaid_blocks = global_state["total_unpaid_blocks"].as(); const asset supply = get_token_supply(); const asset balance = get_balance(prod_name); - const uint32_t unpaid_blocks = get_producer_info(prod_name)["unpaid_blocks"].as(); - const uint64_t usecs_between_fills = claim_time - initial_claim_time; const double expected_supply_growth = initial_supply.amount * double(usecs_between_fills) * cont_rate / usecs_per_year; BOOST_REQUIRE_EQUAL( int64_t(expected_supply_growth), supply.amount - initial_supply.amount ); BOOST_REQUIRE_EQUAL( int64_t(expected_supply_growth) - int64_t(expected_supply_growth)/5, savings - initial_savings ); - const int64_t expected_perblock_bucket = int64_t( initial_supply.amount * double(usecs_between_fills) * (0.25 * cont_rate/ 5.) / usecs_per_year ) + initial_perblock_bucket; - const int64_t expected_pervote_bucket = int64_t( initial_supply.amount * double(usecs_between_fills) * (0.75 * cont_rate/ 5.) / usecs_per_year ) + initial_pervote_bucket; - + const int64_t expected_perblock_bucket = int64_t( initial_supply.amount * double(usecs_between_fills) * (0.25 * cont_rate/ 5.) / usecs_per_year ) + + initial_perblock_bucket; + const int64_t expected_pervote_bucket = int64_t( initial_supply.amount * double(usecs_between_fills) * (0.75 * cont_rate/ 5.) / usecs_per_year ) + + initial_pervote_bucket; const int64_t from_perblock_bucket = initial_unpaid_blocks * expected_perblock_bucket / initial_tot_unpaid_blocks ; const int64_t from_pervote_bucket = int64_t( vote_shares[prod_index] * expected_pervote_bucket); @@ -1852,7 +1849,6 @@ BOOST_FIXTURE_TEST_CASE(multiple_producer_pay, eosio_system_tester, * boost::uni // wait two more hours, now most producers haven't produced in a day and will // be deactivated produce_block(fc::seconds(2 * 3600)); - produce_blocks(8 * 21 * 12); { @@ -1882,6 +1878,15 @@ BOOST_FIXTURE_TEST_CASE(multiple_producer_pay, eosio_system_tester, * boost::uni BOOST_REQUIRE_EQUAL(0, inactive_prod_info["time_became_active"].as()); BOOST_REQUIRE_EQUAL(error("condition: assertion failed: producer does not have an active key"), push_action(producer_names[one_inactive_index], N(claimrewards), mvo()("owner", producer_names[one_inactive_index]))); + // re-register deactivated producer and let him produce blocks again + const uint32_t initial_unpaid_blocks = inactive_prod_info["unpaid_blocks"].as(); + regproducer(producer_names[one_inactive_index]); + produce_blocks(21 * 12); + auto reactivated_prod_info = get_producer_info(producer_names[one_inactive_index]); + const uint32_t unpaid_blocks = reactivated_prod_info["unpaid_blocks"].as(); + BOOST_REQUIRE(initial_unpaid_blocks + 12 <= unpaid_blocks); + BOOST_REQUIRE_EQUAL(success(), + push_action(producer_names[one_inactive_index], N(claimrewards), mvo()("owner", producer_names[one_inactive_index]))); } } FC_LOG_AND_RETHROW() @@ -2067,12 +2072,10 @@ BOOST_FIXTURE_TEST_CASE(producers_upgrade_system_contract, eosio_system_tester) BOOST_FIXTURE_TEST_CASE(producer_onblock_check, eosio_system_tester) try { - const auto tol = boost::test_tools::tolerance(0.0000000001); - const asset large_asset = asset::from_string("80.0000 EOS"); - create_account_with_resources( N(producvotera), N(eosio), asset::from_string("1.0000 EOS"), false, large_asset, large_asset ); - create_account_with_resources( N(producvoterb), N(eosio), asset::from_string("1.0000 EOS"), false, large_asset, large_asset ); - create_account_with_resources( N(producvoterc), N(eosio), asset::from_string("1.0000 EOS"), false, large_asset, large_asset ); + create_account_with_resources( N(producvotera), config::system_account_name, asset::from_string("1.0000 EOS"), false, large_asset, large_asset ); + create_account_with_resources( N(producvoterb), config::system_account_name, asset::from_string("1.0000 EOS"), false, large_asset, large_asset ); + create_account_with_resources( N(producvoterc), config::system_account_name, asset::from_string("1.0000 EOS"), false, large_asset, large_asset ); // create accounts {defproducera, defproducerb, ..., defproducerz} and register as producers std::vector producer_names; @@ -2086,11 +2089,12 @@ BOOST_FIXTURE_TEST_CASE(producer_onblock_check, eosio_system_tester) try { for (auto a:producer_names) regproducer(a); - BOOST_REQUIRE_EQUAL(0, get_producer_info( N(defproducera) )["total_votes"].as()); - BOOST_REQUIRE_EQUAL(0, get_producer_info( N(defproducerz) )["total_votes"].as()); + BOOST_REQUIRE_EQUAL(0, get_producer_info( producer_names.front() )["total_votes"].as()); + BOOST_REQUIRE_EQUAL(0, get_producer_info( producer_names.back() )["total_votes"].as()); - issue( "producvotera", "100000000.0000 EOS", config::system_account_name); - BOOST_REQUIRE_EQUAL(success(), stake("producvotera", "30000000.0000 EOS", "30000000.0000 EOS")); + transfer(config::system_account_name, "producvotera", "200000000.0000 EOS", config::system_account_name); + + BOOST_REQUIRE_EQUAL(success(), stake("producvotera", "70000000.0000 EOS", "70000000.0000 EOS")); BOOST_REQUIRE_EQUAL(success(), push_action(N(producvotera), N(voteproducer), mvo() ("voter", "producvotera") ("proxy", name(0).to_string()) @@ -2116,11 +2120,11 @@ BOOST_FIXTURE_TEST_CASE(producer_onblock_check, eosio_system_tester) try { BOOST_REQUIRE_EQUAL(true, rest_didnt_produce); } - // issue across 15% boundary - issue( "producvoterb", "100000000.0000 EOS", config::system_account_name); - BOOST_REQUIRE_EQUAL(success(), stake("producvoterb", "30000000.0000 EOS", "30000000.0000 EOS")); - issue( "producvoterc", "100000000.0000 EOS", config::system_account_name); - BOOST_REQUIRE_EQUAL(success(), stake("producvoterc", "30000000.0000 EOS", "30000000.0000 EOS")); + // stake across 15% boundary + transfer(config::system_account_name, "producvoterb", "100000000.0000 EOS", config::system_account_name); + BOOST_REQUIRE_EQUAL(success(), stake("producvoterb", "4000000.0000 EOS", "4000000.0000 EOS")); + transfer(config::system_account_name, "producvoterc", "100000000.0000 EOS", config::system_account_name); + BOOST_REQUIRE_EQUAL(success(), stake("producvoterc", "2000000.0000 EOS", "2000000.0000 EOS")); BOOST_REQUIRE_EQUAL(success(), push_action(N(producvoterb), N(voteproducer), mvo() ("voter", "producvoterb")