Skip to content

Commit

Permalink
Implement schedules
Browse files Browse the repository at this point in the history
  • Loading branch information
DenisCarriere committed May 4, 2024
1 parent 7fb34dd commit 812ffa6
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 1 deletion.
46 changes: 46 additions & 0 deletions contracts/eosio.system/include/eosio.system/eosio.system.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,17 @@ namespace eosiosystem {
EOSLIB_SERIALIZE( eosio_global_state4, (continuous_rate)(inflation_pay_factor)(votepay_factor) )
};

// Defines the schedule for pre-determined annual rate changes.
struct [[eosio::table, eosio::contract("eosio.system")]] schedules_info {
time_point_sec start_time;
int64_t annual_rate;

uint64_t primary_key() const { return start_time.sec_since_epoch(); }

// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE( schedules_info, (start_time)(annual_rate) )
};

inline eosio::block_signing_authority convert_to_block_signing_authority( const eosio::public_key& producer_key ) {
return eosio::block_signing_authority_v0{ .threshold = 1, .keys = {{producer_key, 1}} };
}
Expand Down Expand Up @@ -329,6 +340,7 @@ namespace eosiosystem {

typedef eosio::multi_index< "producers2"_n, producer_info2 > producers_table2;

typedef eosio::multi_index< "schedules"_n, schedules_info > schedules_table;

typedef eosio::singleton< "global"_n, eosio_global_state > global_state_singleton;

Expand Down Expand Up @@ -713,6 +725,7 @@ namespace eosiosystem {
eosio_global_state2 _gstate2;
eosio_global_state3 _gstate3;
eosio_global_state4 _gstate4;
schedules_table _schedules;
rammarket _rammarket;
rex_pool_table _rexpool;
rex_return_pool_table _rexretpool;
Expand Down Expand Up @@ -1429,6 +1442,35 @@ namespace eosiosystem {
[[eosio::action]]
void setinflation( int64_t annual_rate, int64_t inflation_pay_factor, int64_t votepay_factor );

/**
* Set the schedule for pre-determined annual rate changes.
*
* @param start_time - the time to start the schedule.
* @param annual_rate - the annual inflation rate of the core token supply.
* (eg. For 5% Annual inflation => annual_rate=500
* For 1.5% Annual inflation => annual_rate=150
*/
[[eosio::action]]
void setschedule( const time_point_sec start_time, int64_t annual_rate );

/**
* Delete the schedule for pre-determined annual rate changes.
*
* @param start_time - the time to start the schedule.
*/
[[eosio::action]]
void delschedule( const time_point_sec start_time );

/**
* Executes the next schedule for pre-determined annual rate changes.
*
* Start time of the schedule must be in the past.
*
* Can be executed by any account.
*/
[[eosio::action]]
void execschedule();

/**
* Facilitates the removal of vested staked tokens from an account, ensuring that these tokens are reallocated to the system's pool.
*
Expand Down Expand Up @@ -1542,6 +1584,9 @@ namespace eosiosystem {
using cfgpowerup_action = eosio::action_wrapper<"cfgpowerup"_n, &system_contract::cfgpowerup>;
using powerupexec_action = eosio::action_wrapper<"powerupexec"_n, &system_contract::powerupexec>;
using powerup_action = eosio::action_wrapper<"powerup"_n, &system_contract::powerup>;
using execschedule_action = eosio::action_wrapper<"execschedule"_n, &system_contract::execschedule>;
using setschedule_action = eosio::action_wrapper<"setschedule"_n, &system_contract::setschedule>;
using delschedule_action = eosio::action_wrapper<"delschedule"_n, &system_contract::delschedule>;

private:
// Implementation details:
Expand All @@ -1557,6 +1602,7 @@ namespace eosiosystem {
static eosio_global_state4 get_default_inflation_parameters();
symbol core_symbol()const;
void update_ram_supply();
bool execute_next_schedule();

// defined in rex.cpp
void runrex( uint16_t max );
Expand Down
38 changes: 37 additions & 1 deletion contracts/eosio.system/ricardian/eosio.system.contracts.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -752,4 +752,40 @@ summary: 'User may powerup to reserve resources'
icon: @ICON_BASE_URL@/@RESOURCE_ICON_URI@
---

Users may use the powerup action to reserve resources.
Users may use the powerup action to reserve resources.

<h1 class="contract">setschedule</h1>

---
spec_version: "0.2.0"
title: Set Annual Rate Schedule
summary: 'Set annual rate parameters'
icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@
---

{{$action.account}} sets a pre-determined inflation schedule to adjust parameters as follows:

* Start time of the schedule: {{start_time}}
* Annual inflation rate (in units of a hundredth of a percent): {{annual_rate}}

<h1 class="contract">delschedule</h1>

---
spec_version: "0.2.0"
title: Delete Annual Rate Schedule
summary: 'Delete annual rate schedule'
icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@
---

{{$action.account}} to delete a pre-determined inflation schedule from {{start_time}} start time.

<h1 class="contract">execschedule</h1>

---
spec_version: "0.2.0"
title: Execute Next Annual Rate Schedule
summary: 'Execute next annual rate schedule'
icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@
---

{{$action.account}} to execute the next upcoming annual rate schedule.
47 changes: 47 additions & 0 deletions contracts/eosio.system/src/eosio.system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ namespace eosiosystem {
_global2(get_self(), get_self().value),
_global3(get_self(), get_self().value),
_global4(get_self(), get_self().value),
_schedules(get_self(), get_self().value),
_rammarket(get_self(), get_self().value),
_rexpool(get_self(), get_self().value),
_rexretpool(get_self(), get_self().value),
Expand Down Expand Up @@ -423,6 +424,52 @@ namespace eosiosystem {
_global4.set( _gstate4, get_self() );
}

void system_contract::setschedule( const time_point_sec start_time, int64_t annual_rate )
{
require_auth( get_self() );

check(annual_rate >= 0, "annual_rate can't be negative");

auto itr = _schedules.find( start_time.sec_since_epoch() );

if( itr == _schedules.end() ) {
_schedules.emplace( get_self(), [&]( auto& s ) {
s.start_time = start_time;
s.annual_rate = annual_rate;
});
} else {
_schedules.modify( itr, same_payer, [&]( auto& s ) {
check( annual_rate != s.annual_rate, "annual_rate was not modified");
s.annual_rate = annual_rate;
});
}
}

void system_contract::delschedule( const time_point_sec start_time )
{
require_auth( get_self() );

auto itr = _schedules.require_find( start_time.sec_since_epoch(), "schedule not found" );
_schedules.erase( itr );
}

void system_contract::execschedule()
{
check(execute_next_schedule(), "no schedule to execute");
}

bool system_contract::execute_next_schedule()
{
auto itr = _schedules.begin();
if ( itr->annual_rate == 0 ) return false; // row is empty
if ( current_time_point().sec_since_epoch() >= itr->start_time.sec_since_epoch() ) {
_gstate4.continuous_rate = get_continuous_rate(itr->annual_rate);
_schedules.erase( itr );
return true;
}
return false;
}

/**
* Called after a new account is created. This code enforces resource-limits rules
* for new accounts as well as new account naming conventions.
Expand Down
1 change: 1 addition & 0 deletions contracts/eosio.system/src/producer_pay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ namespace eosiosystem {
void system_contract::claimrewards( const name& owner ) {
require_auth( owner );

execute_next_schedule();
const auto& prod = _producers.get( owner.value );
check( prod.active(), "producer does not have an active key" );

Expand Down
54 changes: 54 additions & 0 deletions tests/eosio.system_schedules_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include <boost/test/unit_test.hpp>

#include "eosio.system_tester.hpp"

using namespace eosio_system;

BOOST_AUTO_TEST_SUITE(eosio_system_vesting_tests)

BOOST_FIXTURE_TEST_CASE(set_schedules, eosio_system_tester) try {

const std::vector<account_name> accounts = { "alice"_n };
create_accounts_with_resources( accounts );
const account_name alice = accounts[0];

const uint32_t YEAR = 86400 * 365;
const uint32_t initial_start_time = 1577836856; // 2020-01-01T00:00
const time_point_sec start_time = time_point_sec(initial_start_time);

// action validation
BOOST_REQUIRE_EQUAL( success(), setschedule(start_time, 500) ); // 5% annual rate
BOOST_REQUIRE_EQUAL( wasm_assert_msg("annual_rate was not modified"), setschedule(start_time, 500) );
BOOST_REQUIRE_EQUAL( wasm_assert_msg("annual_rate can't be negative"), setschedule(start_time, -1) );
BOOST_REQUIRE_EQUAL( wasm_assert_msg("schedule not found"), delschedule(time_point_sec(0)) );
BOOST_REQUIRE_EQUAL( success(), setschedule(start_time, 200) ); // allow override existing schedules
BOOST_REQUIRE_EQUAL( success(), delschedule(start_time) );

// set 3 future schedules
BOOST_REQUIRE_EQUAL( success(), setschedule(time_point_sec(start_time), 200) );
BOOST_REQUIRE_EQUAL( success(), setschedule(time_point_sec(initial_start_time + YEAR * 4), 100) );
BOOST_REQUIRE_EQUAL( success(), setschedule(time_point_sec(initial_start_time + YEAR * 8), 50) );

// current state prior to first schedule
const double before = get_global_state4()["continuous_rate"].as_double();
BOOST_REQUIRE_EQUAL( before, 0.048790164169432007 ); // 5% annual rate
const uint32_t current_time_sec = control->pending_block_time().sec_since_epoch();
BOOST_REQUIRE_EQUAL( current_time_sec, initial_start_time ); // 2020-01-01T00:00

// advance to halvening schedules
produce_block( fc::days(365 * 4) ); // advance to year 4
BOOST_REQUIRE_EQUAL( control->pending_block_time().sec_since_epoch(), initial_start_time + YEAR * 4 ); // 2024-01-01T00:00
BOOST_REQUIRE_EQUAL( success(), execschedule(alice) );
BOOST_REQUIRE_EQUAL( get_global_state4()["continuous_rate"].as_double(), 0.019802627296179712 ); // 2% annual rate

produce_block( fc::days(365 * 4) ); // advanced to year 8
BOOST_REQUIRE_EQUAL( success(), execschedule(alice) );
BOOST_REQUIRE_EQUAL( get_global_state4()["continuous_rate"].as_double(), 0.0099503308531680833 ); // 1% annual rate

produce_block( fc::days(365 * 4) ); // advanced to year 12
BOOST_REQUIRE_EQUAL( success(), execschedule(alice) );
BOOST_REQUIRE_EQUAL( get_global_state4()["continuous_rate"].as_double(), 0.0049875415110390738 ); // 0.5% annual rate

} FC_LOG_AND_RETHROW()

BOOST_AUTO_TEST_SUITE_END()
22 changes: 22 additions & 0 deletions tests/eosio.system_tester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1271,6 +1271,11 @@ class eosio_system_tester : public TESTER {
return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "eosio_global_state3", data, abi_serializer::create_yield_function(abi_serializer_max_time) );
}

fc::variant get_global_state4() {
vector<char> data = get_row_by_account( config::system_account_name, config::system_account_name, "global4"_n, "global4"_n );
return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "eosio_global_state4", data, abi_serializer::create_yield_function(abi_serializer_max_time) );
}

fc::variant get_refund_request( name account ) {
vector<char> data = get_row_by_account( config::system_account_name, account, "refunds"_n, account );
return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "refund_request", data, abi_serializer::create_yield_function(abi_serializer_max_time) );
Expand Down Expand Up @@ -1406,6 +1411,23 @@ class eosio_system_tester : public TESTER {
);
}

action_result setschedule( const time_point_sec start_time, int64_t annual_rate ) {
return push_action( "eosio"_n, "setschedule"_n, mvo()
("start_time", start_time)
("annual_rate", annual_rate)
);
}

action_result delschedule( const time_point_sec start_time ) {
return push_action( "eosio"_n, "delschedule"_n, mvo()
("start_time", start_time)
);
}

action_result execschedule( const name executor ) {
return push_action( executor, "execschedule"_n, mvo());
}

abi_serializer abi_ser;
abi_serializer token_abi_ser;
};
Expand Down

0 comments on commit 812ffa6

Please sign in to comment.