diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index e50d4cab..0e9aed33 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -226,6 +226,7 @@ void_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator: // Upgrade to lifetime member. I don't care what the account was before. a.membership_expiration_date = time_point_sec::maximum(); a.referrer = a.registrar = a.lifetime_referrer = a.get_id(); + a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - a.network_fee_percentage; } else if( a.is_annual_member(d.head_block_time()) ) { // Renew an annual subscription that's still in effect. FC_ASSERT(a.membership_expiration_date - d.head_block_time() < fc::days(3650), diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index ffc04909..1b2e343f 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -155,6 +155,8 @@ void database::init_genesis(const genesis_allocation& initial_allocation) const account_object& genesis_account = create( [&](account_object& n) { n.membership_expiration_date = time_point_sec::maximum(); + n.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; + n.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; n.name = "genesis"; n.owner.add_authority(genesis_key.get_id(), 1); n.owner.weight_threshold = 1; @@ -179,6 +181,8 @@ void database::init_genesis(const genesis_allocation& initial_allocation) a.referrer = account_id_type(i); a.registrar = account_id_type(i); a.lifetime_referrer = account_id_type(i); + a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; + a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; a.membership_expiration_date = fc::time_point_sec::maximum(); a.name = string("init") + fc::to_string(i); a.statistics = stats_obj.id; diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index b0adbd1c..84a3597d 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -242,10 +242,10 @@ void database::process_budget() // blocks_to_maint > 0 because time_to_maint > 0, // which means numerator is at least equal to block_interval - share_type available_funds = get_max_budget( now ); + share_type available_funds = get_max_budget(now); share_type witness_budget = gpo.parameters.witness_pay_per_block.value * blocks_to_maint; - witness_budget = std::min( witness_budget, available_funds ); + witness_budget = std::min(witness_budget, available_funds); available_funds -= witness_budget; fc::uint128_t worker_budget_u128 = gpo.parameters.worker_budget_per_day.value; @@ -260,21 +260,22 @@ void database::process_budget() available_funds -= worker_budget; share_type leftover_worker_funds = worker_budget; - pay_workers( leftover_worker_funds ); + pay_workers(leftover_worker_funds); available_funds += leftover_worker_funds; - modify( core, [&]( asset_dynamic_data_object& _core ) + modify(core, [&]( asset_dynamic_data_object& _core ) { _core.current_supply = (_core.current_supply + witness_budget + worker_budget - leftover_worker_funds - _core.accumulated_fees); _core.accumulated_fees = 0; - } ); - modify( dpo, [&]( dynamic_global_property_object& _dpo ) + }); + modify(dpo, [&]( dynamic_global_property_object& _dpo ) { + // Should this be +=? _dpo.witness_budget = witness_budget; _dpo.last_budget_time = now; - } ); + }); // available_funds is money we could spend, but don't want to. // we simply let it evaporate back into the reserve. @@ -361,6 +362,11 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g auto cut_fee = [](share_type a, uint16_t p) -> share_type { + if( a == 0 || p == 0 ) + return 0; + if( p == GRAPHENE_100_PERCENT ) + return a; + fc::uint128 r(a.value); r *= p; r /= GRAPHENE_100_PERCENT; @@ -400,10 +406,11 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g share_type burned = cut_fee(network_cut, props.parameters.burn_percent_of_fee); share_type accumulated = network_cut - burned; assert( accumulated + burned == network_cut ); - share_type referral = core_fee_total - network_cut; + share_type lifetime_cut = cut_fee(core_fee_total, a.lifetime_referrer_fee_percentage); + share_type referral = core_fee_total - network_cut - lifetime_cut; - d.modify(dynamic_asset_data_id_type()(d), [burned,accumulated](asset_dynamic_data_object& d) { - d.accumulated_fees += accumulated + burned; + d.modify(dynamic_asset_data_id_type()(d), [network_cut](asset_dynamic_data_object& d) { + d.accumulated_fees += network_cut; }); d.modify(a.statistics(d), [core_fee_total](account_statistics_object& s) { @@ -413,20 +420,18 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g d.deposit_cashback( a, bulk_cashback ); - assert( referral + bulk_cashback + accumulated + burned == core_fee_subtotal ); - // Potential optimization: Skip some of this math and object lookups by special casing on the account type. // For example, if the account is a lifetime member, we can skip all this and just deposit the referral to // it directly. - share_type lifetime_cut = cut_fee(referral, a.lifetime_referrer_fee_percentage); share_type referrer_cut = cut_fee(referral, a.referrer_rewards_percentage); - share_type registrar_cut = referral - lifetime_cut = referrer_cut; + share_type registrar_cut = referral - referrer_cut; d.deposit_cashback(d.get(a.lifetime_referrer), lifetime_cut); d.deposit_cashback(d.get(a.referrer), referrer_cut); d.deposit_cashback(d.get(a.registrar), registrar_cut); - assert( lifetime_cut + referrer_cut + registrar_cut == referral ); + idump((referrer_cut)(registrar_cut)(bulk_cashback)(accumulated)(burned)(lifetime_cut)(core_fee_subtotal)); + assert( referrer_cut + registrar_cut + bulk_cashback + accumulated + burned + lifetime_cut == core_fee_subtotal ); } } } fees_helper(*this, gpo); diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 75ca540c..1efea9bc 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -81,7 +81,6 @@ #define GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC (60*60*24*365) ///< 1 year #define GRAPHENE_DEFAULT_CASHBACK_VESTING_THRESHOLD (GRAPHENE_BLOCKCHAIN_PRECISION*int64_t(100)) #define GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE (20*GRAPHENE_1_PERCENT) -#define GRAPHENE_DEFAULT_WITNESS_PAY_PERCENT_OF_ACCUMULATED ( 1728000) /// gives a half life of 1 year assuming 1 second blocks #define GRAPHENE_WITNESS_PAY_PERCENT_PRECISION (1000000000) #define GRAPHENE_GENESIS_TIMESTAMP (1431700000) /// Should be divisible by GRAPHENE_DEFAULT_BLOCK_INTERVAL diff --git a/libraries/chain/include/graphene/chain/transaction.hpp b/libraries/chain/include/graphene/chain/transaction.hpp index 7f947b70..a048fbeb 100644 --- a/libraries/chain/include/graphene/chain/transaction.hpp +++ b/libraries/chain/include/graphene/chain/transaction.hpp @@ -19,6 +19,8 @@ #include #include +#include + // this is for htonl() and ntohl() functions // TODO: write and use FC wrappers for these functions #ifndef WIN32 diff --git a/libraries/chain/include/graphene/chain/types.hpp b/libraries/chain/include/graphene/chain/types.hpp index 75075e00..504a059c 100644 --- a/libraries/chain/include/graphene/chain/types.hpp +++ b/libraries/chain/include/graphene/chain/types.hpp @@ -416,8 +416,7 @@ namespace graphene { namespace chain { struct chain_parameters { - fee_schedule_type current_fees; ///< current schedule of fees, indexed by @ref fee_type - uint32_t witness_pay_percent_of_accumulated = GRAPHENE_DEFAULT_WITNESS_PAY_PERCENT_OF_ACCUMULATED; ///< percentage of accumulated fees in core asset to pay to witnesses for block production + fee_schedule_type current_fees; ///< current schedule of fees uint8_t block_interval = GRAPHENE_DEFAULT_BLOCK_INTERVAL; ///< interval in seconds between blocks uint32_t maintenance_interval = GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL; ///< interval in sections between blockchain maintenance events uint32_t maximum_transaction_size = GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE; ///< maximum allowable size in bytes for a transaction @@ -454,7 +453,6 @@ namespace graphene { namespace chain { FC_ASSERT( bulk_discount_threshold_min <= bulk_discount_threshold_max ); FC_ASSERT( bulk_discount_threshold_min > 0 ); - FC_ASSERT( witness_pay_percent_of_accumulated < GRAPHENE_WITNESS_PAY_PERCENT_PRECISION ); FC_ASSERT( block_interval <= GRAPHENE_MAX_BLOCK_INTERVAL ); FC_ASSERT( block_interval > 0 ); FC_ASSERT( maintenance_interval > block_interval, @@ -575,7 +573,6 @@ FC_REFLECT( graphene::chain::fee_schedule_type, FC_REFLECT( graphene::chain::chain_parameters, (current_fees) - (witness_pay_percent_of_accumulated) (block_interval) (maintenance_interval) (maximum_transaction_size) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 3af8a3be..2d0d6dec 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -763,9 +763,11 @@ public: account_object account_obj = get_account(name); FC_ASSERT( !account_obj.is_lifetime_member() ); - // TODO - signed_transaction tx; + account_upgrade_operation op; + op.account_to_upgrade = account_obj.get_id(); + op.upgrade_to_lifetime_member = true; + tx.operations = {op}; tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) ); tx.validate(); diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index c602200f..6f74f777 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -119,6 +119,7 @@ void database_fixture::verify_asset_supplies( )const for( const account_statistics_object& a : statistics_index ) { reported_core_in_orders += a.total_core_in_orders; + total_balances[asset_id_type()] += a.pending_fees; } for( const limit_order_object& o : db.get_index_type().indices() ) { @@ -672,7 +673,8 @@ void database_fixture::enable_fees( { fc::reflector::visit(fee_schedule_type::fee_set_visitor{gpo.parameters.current_fees, uint32_t(fee.value)}); - gpo.parameters.current_fees.membership_annual_fee = 10*fee.value; + gpo.parameters.current_fees.membership_annual_fee = 3*fee.value; + gpo.parameters.current_fees.membership_lifetime_fee = 10*fee.value; } ); } @@ -685,9 +687,12 @@ void database_fixture::upgrade_to_lifetime_member( const account_object& account { try { - // TODO + account_upgrade_operation op; + op.account_to_upgrade = account.get_id(); + op.upgrade_to_lifetime_member = true; + trx.operations = {op}; db.push_transaction( trx, ~0 ); - FC_ASSERT( account.is_lifetime_member() ); + FC_ASSERT( op.account_to_upgrade(db).is_lifetime_member() ); trx.clear(); } FC_CAPTURE_AND_RETHROW((account)) diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 8aac3ef0..03585f4b 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -816,6 +816,7 @@ BOOST_FIXTURE_TEST_CASE( max_authority_membership, database_fixture ) private_key_type sam_key = generate_private_key("sam"); account_object sam_account_object = create_account( "sam", sam_key ); + upgrade_to_lifetime_member(sam_account_object); account_object genesis_account_object = genesis_account(db); const asset_object& core = asset_id_type()(db); diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 17b76f06..dd669d51 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -107,6 +107,7 @@ BOOST_AUTO_TEST_CASE( child_account ) const auto& nathan_key = register_key(nathan_private_key.get_public_key()); const account_object& nathan = get_account("nathan"); const account_object& root = create_account("root"); + upgrade_to_lifetime_member(root); skip_key_index_test = true; db.modify(nathan, [nathan_key](account_object& a) { @@ -128,7 +129,7 @@ BOOST_AUTO_TEST_CASE( child_account ) BOOST_REQUIRE_THROW(db.push_transaction(trx), fc::exception); trx.signatures.clear(); op.owner = authority(1, account_id_type(nathan.id), 1); - trx.operations.back() = op; + trx.operations = {op}; sign(trx, key_id_type(), fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")))); sign(trx, nathan_key.id, nathan_private_key); db.push_transaction(trx); @@ -186,13 +187,16 @@ BOOST_AUTO_TEST_CASE( update_account ) transfer(account_id_type()(db), nathan, asset(3000000)); enable_fees(); - // TODO: op.upgrade_to_prime = true; - op.fee = op.calculate_fee( db.get_global_properties().parameters.current_fees ); - trx.operations.push_back(op); - db.push_transaction(trx, ~0); + { + account_upgrade_operation op; + op.account_to_upgrade = nathan.id; + op.upgrade_to_lifetime_member = true; + op.fee = op.calculate_fee(db.get_global_properties().parameters.current_fees); + trx.operations = {op}; + db.push_transaction(trx, ~0); + } - BOOST_CHECK( nathan.referrer == nathan.id ); - BOOST_CHECK( nathan.referrer_rewards_percentage == 100 ); + BOOST_CHECK( nathan.is_lifetime_member() ); } catch (fc::exception& e) { edump((e.to_detail_string())); throw; @@ -1866,13 +1870,13 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) const asset_object* core = &asset_id_type()(db); const account_object* nathan = &get_account("nathan"); - enable_fees(100000000); + enable_fees(105000000); BOOST_CHECK_GT(db.current_fee_schedule().membership_lifetime_fee, 0); BOOST_CHECK_EQUAL(core->dynamic_asset_data_id(db).accumulated_fees.value, 0); - account_update_operation uop; - uop.account = nathan->get_id(); - // TODO: uop.upgrade_to_prime = true; + account_upgrade_operation uop; + uop.account_to_upgrade = nathan->get_id(); + uop.upgrade_to_lifetime_member = true; trx.set_expiration(db.head_block_id()); trx.operations.push_back(uop); trx.visit(operation_set_fee(db.current_fee_schedule())); @@ -1880,17 +1884,13 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) trx.sign(key_id_type(),generate_private_key("genesis")); db.push_transaction(trx); trx.clear(); - BOOST_CHECK_EQUAL(get_balance(*nathan, *core), 9000000000); - BOOST_CHECK_EQUAL(core->dynamic_asset_data_id(db).accumulated_fees.value, 210000000); - // TODO: Replace this with another check - //BOOST_CHECK_EQUAL(account_id_type()(db).statistics(db).cashback_rewards.value, 1000000000-210000000); + BOOST_CHECK_EQUAL(get_balance(*nathan, *core), 8950000000); generate_block(); nathan = &get_account("nathan"); core = &asset_id_type()(db); const witness_object* witness = &db.fetch_block_by_number(db.head_block_num())->witness(db); - BOOST_CHECK_GT(core->dynamic_asset_data_id(db).accumulated_fees.value, 0); BOOST_CHECK_EQUAL(witness->accumulated_income.value, 0); auto schedule_maint = [&]() @@ -1900,7 +1900,7 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) { _dpo.next_maintenance_time = db.head_block_time() + 1; } ); - } ; + }; // generate some blocks while( db.head_block_num() < 30 ) @@ -1913,6 +1913,8 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) // maintenance will be in block 31. time of block 31 - time of block 1 = 30 * 5 seconds. schedule_maint(); + // TODO: Replace this with another check + //BOOST_CHECK_EQUAL(account_id_type()(db).statistics(db).cashback_rewards.value, 1000000000-200000000); // first witness paid from old budget (so no pay) BOOST_CHECK_EQUAL( core->burned(db).value, 0 ); generate_block();