Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[framework] Refactor reward rate calculation #7867

Merged
merged 1 commit into from
Apr 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions aptos-move/e2e-move-tests/src/tests/stake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,13 +245,12 @@ fn test_staking_rewards() {
rewards_rate_denominator,
);

// Another 0.5 year passed. Rewards rate halves. New rewards rate is 0.5% every epoch.
// Another 0.5 year passed. Rewards rate halves. New rewards after this epoch rate is 0.5% every epoch.
// Both validators propose a block in the current epoch. Both should receive rewards.
harness.new_block_with_metadata(validator_1_address, vec![]);
harness.new_block_with_metadata(validator_2_address, vec![]);
harness.fast_forward(one_year_in_secs / 2);
harness.new_epoch();
rewards_rate = rewards_rate * rewards_rate_decrease_rate_bps / bps_denominator;
update_stake_amount_and_assert_with_errors(
&mut harness,
&mut stake_amount_1,
Expand All @@ -266,12 +265,32 @@ fn test_staking_rewards() {
rewards_rate,
rewards_rate_denominator,
);
rewards_rate = rewards_rate * rewards_rate_decrease_rate_bps / bps_denominator;

// Another new epoch, both validators receive rewards in 0.5% every epoch.
// Another year passed. Rewards rate halves but it cannot be lower than 0.3%.
// New rewards rate is 0.3% every epoch.
// New rewards rate of the next epoch is 0.3% every epoch.
harness.new_block_with_metadata(validator_1_address, vec![]);
harness.new_block_with_metadata(validator_2_address, vec![]);
harness.fast_forward(one_year_in_secs);
harness.new_epoch();
update_stake_amount_and_assert_with_errors(
&mut harness,
&mut stake_amount_1,
validator_1_address,
rewards_rate,
rewards_rate_denominator,
);
update_stake_amount_and_assert_with_errors(
&mut harness,
&mut stake_amount_2,
validator_2_address,
rewards_rate,
rewards_rate_denominator,
);

// Validator 1 misses one proposal but has one successful so they receive half of the rewards.
harness.new_block_with_metadata(validator_1_address, vec![index_1]);
harness.fast_forward(one_year_in_secs);
harness.new_epoch();
rewards_rate = min_rewards_rate / 2;
update_stake_amount_and_assert_with_errors(
Expand Down
2 changes: 1 addition & 1 deletion aptos-move/framework/aptos-framework/doc/gas_schedule.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ This can be called by on-chain governance to update the gas schedule.



<pre><code><b>pragma</b> timeout = 60;
<pre><code><b>pragma</b> timeout = 100;
<b>requires</b> <b>exists</b>&lt;<a href="stake.md#0x1_stake_ValidatorFees">stake::ValidatorFees</a>&gt;(@aptos_framework);
<b>requires</b> <b>exists</b>&lt;CoinInfo&lt;AptosCoin&gt;&gt;(@aptos_framework);
<b>include</b> <a href="system_addresses.md#0x1_system_addresses_AbortsIfNotAptosFramework">system_addresses::AbortsIfNotAptosFramework</a>{ <a href="account.md#0x1_account">account</a>: aptos_framework };
Expand Down
25 changes: 7 additions & 18 deletions aptos-move/framework/aptos-framework/doc/stake.md
Original file line number Diff line number Diff line change
Expand Up @@ -3060,6 +3060,11 @@ power.

validator_index = validator_index + 1;
};

<b>if</b> (<a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_periodical_reward_rate_decrease_enabled">features::periodical_reward_rate_decrease_enabled</a>()) {
// Update rewards rate after reward distribution.
<a href="staking_config.md#0x1_staking_config_calculate_and_save_latest_epoch_rewards_rate">staking_config::calculate_and_save_latest_epoch_rewards_rate</a>();
};
}
</code></pre>

Expand Down Expand Up @@ -3102,23 +3107,7 @@ This function shouldn't abort.
<b>assume</b> cur_validator_perf.successful_proposals + cur_validator_perf.failed_proposals &lt;= <a href="stake.md#0x1_stake_MAX_U64">MAX_U64</a>;
};
<b>let</b> num_total_proposals = cur_validator_perf.successful_proposals + cur_validator_perf.failed_proposals;
<b>let</b> (rewards_rate, rewards_rate_denominator) = <b>if</b> (<a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_periodical_reward_rate_decrease_enabled">features::periodical_reward_rate_decrease_enabled</a>()) {
<b>let</b> epoch_rewards_rate = <a href="staking_config.md#0x1_staking_config_calculate_and_save_latest_epoch_rewards_rate">staking_config::calculate_and_save_latest_epoch_rewards_rate</a>();
<b>if</b> (<a href="../../aptos-stdlib/doc/fixed_point64.md#0x1_fixed_point64_is_zero">fixed_point64::is_zero</a>(epoch_rewards_rate)) {
(0u64, 1u64)
} <b>else</b> {
// Maximize denominator for higher precision.
// Restriction: nominator &lt;= <a href="stake.md#0x1_stake_MAX_REWARDS_RATE">MAX_REWARDS_RATE</a> && denominator &lt;= <a href="stake.md#0x1_stake_MAX_U64">MAX_U64</a>
<b>let</b> denominator = <a href="../../aptos-stdlib/doc/fixed_point64.md#0x1_fixed_point64_divide_u128">fixed_point64::divide_u128</a>((<a href="stake.md#0x1_stake_MAX_REWARDS_RATE">MAX_REWARDS_RATE</a> <b>as</b> u128), epoch_rewards_rate);
<b>if</b> (denominator &gt; <a href="stake.md#0x1_stake_MAX_U64">MAX_U64</a>) {
denominator = <a href="stake.md#0x1_stake_MAX_U64">MAX_U64</a>
};
<b>let</b> nominator = (<a href="../../aptos-stdlib/doc/fixed_point64.md#0x1_fixed_point64_multiply_u128">fixed_point64::multiply_u128</a>(denominator, epoch_rewards_rate) <b>as</b> u64);
(nominator, (denominator <b>as</b> u64))
}
} <b>else</b> {
<a href="staking_config.md#0x1_staking_config_get_reward_rate">staking_config::get_reward_rate</a>(<a href="staking_config.md#0x1_staking_config">staking_config</a>)
};
<b>let</b> (rewards_rate, rewards_rate_denominator) = <a href="staking_config.md#0x1_staking_config_get_reward_rate">staking_config::get_reward_rate</a>(<a href="staking_config.md#0x1_staking_config">staking_config</a>);
<b>let</b> rewards_active = <a href="stake.md#0x1_stake_distribute_rewards">distribute_rewards</a>(
&<b>mut</b> stake_pool.active,
num_successful_proposals,
Expand Down Expand Up @@ -4229,7 +4218,7 @@ Returns validator's next epoch voting power, including pending_active, active, a
<b>requires</b> <b>exists</b>&lt;<a href="stake.md#0x1_stake_ValidatorPerformance">ValidatorPerformance</a>&gt;(@aptos_framework);
<b>requires</b> <b>exists</b>&lt;<a href="stake.md#0x1_stake_ValidatorSet">ValidatorSet</a>&gt;(@aptos_framework);
<b>requires</b> <b>exists</b>&lt;StakingConfig&gt;(@aptos_framework);
<b>requires</b> <b>exists</b>&lt;StakingRewardsConfig&gt;(@aptos_framework) || !<a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_spec_reward_rate_decrease_enabled">features::spec_reward_rate_decrease_enabled</a>();
<b>requires</b> <b>exists</b>&lt;StakingRewardsConfig&gt;(@aptos_framework) || !<a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_spec_periodical_reward_rate_decrease_enabled">features::spec_periodical_reward_rate_decrease_enabled</a>();
<b>requires</b> <b>exists</b>&lt;<a href="timestamp.md#0x1_timestamp_CurrentTimeMicroseconds">timestamp::CurrentTimeMicroseconds</a>&gt;(@aptos_framework);
<b>requires</b> <b>exists</b>&lt;<a href="stake.md#0x1_stake_ValidatorFees">ValidatorFees</a>&gt;(@aptos_framework);
}
Expand Down
42 changes: 33 additions & 9 deletions aptos-move/framework/aptos-framework/doc/staking_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,15 @@ Staking reward configurations that will be stored with the @aptos_framework acco
## Constants


<a name="0x1_staking_config_MAX_U64"></a>



<pre><code><b>const</b> <a href="staking_config.md#0x1_staking_config_MAX_U64">MAX_U64</a>: u128 = 18446744073709551615;
</code></pre>



<a name="0x1_staking_config_BPS_DENOMINATOR"></a>

Denominator of number in basis points. 1 bps(basis points) = 0.01%.
Expand Down Expand Up @@ -536,8 +545,7 @@ withdraw all funds).

## Function `get_reward_rate`

DEPRECATING
Return the reward rate.
Return the reward rate of this epoch.


<pre><code><b>public</b> <b>fun</b> <a href="staking_config.md#0x1_staking_config_get_reward_rate">get_reward_rate</a>(config: &<a href="staking_config.md#0x1_staking_config_StakingConfig">staking_config::StakingConfig</a>): (u64, u64)
Expand All @@ -549,9 +557,25 @@ Return the reward rate.
<summary>Implementation</summary>


<pre><code><b>public</b> <b>fun</b> <a href="staking_config.md#0x1_staking_config_get_reward_rate">get_reward_rate</a>(config: &<a href="staking_config.md#0x1_staking_config_StakingConfig">StakingConfig</a>): (u64, u64) {
<b>assert</b>!(!<a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_periodical_reward_rate_decrease_enabled">features::periodical_reward_rate_decrease_enabled</a>(), <a href="staking_config.md#0x1_staking_config_EDEPRECATED_FUNCTION">EDEPRECATED_FUNCTION</a>);
(config.rewards_rate, config.rewards_rate_denominator)
<pre><code><b>public</b> <b>fun</b> <a href="staking_config.md#0x1_staking_config_get_reward_rate">get_reward_rate</a>(config: &<a href="staking_config.md#0x1_staking_config_StakingConfig">StakingConfig</a>): (u64, u64) <b>acquires</b> <a href="staking_config.md#0x1_staking_config_StakingRewardsConfig">StakingRewardsConfig</a> {
<b>let</b> (rewards_rate, rewards_rate_denominator) = <b>if</b> (<a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_periodical_reward_rate_decrease_enabled">features::periodical_reward_rate_decrease_enabled</a>()) {
<b>let</b> epoch_rewards_rate = <b>borrow_global</b>&lt;<a href="staking_config.md#0x1_staking_config_StakingRewardsConfig">StakingRewardsConfig</a>&gt;(@aptos_framework).rewards_rate;
<b>if</b> (<a href="../../aptos-stdlib/doc/fixed_point64.md#0x1_fixed_point64_is_zero">fixed_point64::is_zero</a>(epoch_rewards_rate)) {
(0u64, 1u64)
} <b>else</b> {
// Maximize denominator for higher precision.
// Restriction: nominator &lt;= <a href="staking_config.md#0x1_staking_config_MAX_REWARDS_RATE">MAX_REWARDS_RATE</a> && denominator &lt;= <a href="staking_config.md#0x1_staking_config_MAX_U64">MAX_U64</a>
<b>let</b> denominator = <a href="../../aptos-stdlib/doc/fixed_point64.md#0x1_fixed_point64_divide_u128">fixed_point64::divide_u128</a>((<a href="staking_config.md#0x1_staking_config_MAX_REWARDS_RATE">MAX_REWARDS_RATE</a> <b>as</b> u128), epoch_rewards_rate);
<b>if</b> (denominator &gt; <a href="staking_config.md#0x1_staking_config_MAX_U64">MAX_U64</a>) {
denominator = <a href="staking_config.md#0x1_staking_config_MAX_U64">MAX_U64</a>
};
<b>let</b> nominator = (<a href="../../aptos-stdlib/doc/fixed_point64.md#0x1_fixed_point64_multiply_u128">fixed_point64::multiply_u128</a>(denominator, epoch_rewards_rate) <b>as</b> u64);
(nominator, (denominator <b>as</b> u64))
}
} <b>else</b> {
(config.rewards_rate, config.rewards_rate_denominator)
};
(rewards_rate, rewards_rate_denominator)
}
</code></pre>

Expand Down Expand Up @@ -1146,7 +1170,7 @@ StakingRewardsConfig does not exist under the aptos_framework before creating it



<pre><code><b>aborts_if</b> <a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_spec_reward_rate_decrease_enabled">features::spec_reward_rate_decrease_enabled</a>();
<pre><code><b>include</b> <a href="staking_config.md#0x1_staking_config_StakingRewardsConfigRequirement">StakingRewardsConfigRequirement</a>;
</code></pre>


Expand All @@ -1163,7 +1187,7 @@ StakingRewardsConfig does not exist under the aptos_framework before creating it


<pre><code><b>aborts_if</b> !<b>exists</b>&lt;<a href="staking_config.md#0x1_staking_config_StakingRewardsConfig">StakingRewardsConfig</a>&gt;(@aptos_framework);
<b>aborts_if</b> !<a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_spec_reward_rate_decrease_enabled">features::spec_reward_rate_decrease_enabled</a>();
<b>aborts_if</b> !<a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_spec_periodical_reward_rate_decrease_enabled">features::spec_periodical_reward_rate_decrease_enabled</a>();
<b>include</b> <a href="staking_config.md#0x1_staking_config_StakingRewardsConfigRequirement">StakingRewardsConfigRequirement</a>;
</code></pre>

Expand All @@ -1180,7 +1204,7 @@ StakingRewardsConfig does not exist under the aptos_framework before creating it



<pre><code><b>requires</b> <a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_spec_reward_rate_decrease_enabled">features::spec_reward_rate_decrease_enabled</a>();
<pre><code><b>requires</b> <a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_spec_periodical_reward_rate_decrease_enabled">features::spec_periodical_reward_rate_decrease_enabled</a>();
<b>include</b> <a href="staking_config.md#0x1_staking_config_StakingRewardsConfigRequirement">StakingRewardsConfigRequirement</a>;
<b>aborts_if</b> !<b>exists</b>&lt;<a href="staking_config.md#0x1_staking_config_StakingRewardsConfig">StakingRewardsConfig</a>&gt;(@aptos_framework);
</code></pre>
Expand Down Expand Up @@ -1247,7 +1271,7 @@ The <code>rewards_rate</code> which is the numerator is limited to be <code>&lt;
rewards_rate/rewards_rate_denominator <= 1.


<pre><code><b>aborts_if</b> <a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_spec_reward_rate_decrease_enabled">features::spec_reward_rate_decrease_enabled</a>();
<pre><code><b>aborts_if</b> <a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_spec_periodical_reward_rate_decrease_enabled">features::spec_periodical_reward_rate_decrease_enabled</a>();
<b>let</b> addr = <a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer_address_of">signer::address_of</a>(aptos_framework);
<b>aborts_if</b> addr != @aptos_framework;
<b>aborts_if</b> new_rewards_rate_denominator &lt;= 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ spec aptos_framework::gas_schedule {
use aptos_framework::transaction_fee;
use aptos_framework::staking_config;

pragma timeout = 60;
pragma timeout = 100;

requires exists<stake::ValidatorFees>(@aptos_framework);
requires exists<CoinInfo<AptosCoin>>(@aptos_framework);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ module aptos_framework::staking_config {
/// 1 year => 365 * 24 * 60 * 60
const ONE_YEAR_IN_SECS: u64 = 31536000;

const MAX_U64: u128 = 18446744073709551615;


/// Validator set configurations that will be stored with the @aptos_framework account.
struct StakingConfig has copy, drop, key {
Expand Down Expand Up @@ -184,11 +186,26 @@ module aptos_framework::staking_config {
config.recurring_lockup_duration_secs
}

/// DEPRECATING
/// Return the reward rate.
public fun get_reward_rate(config: &StakingConfig): (u64, u64) {
assert!(!features::periodical_reward_rate_decrease_enabled(), EDEPRECATED_FUNCTION);
(config.rewards_rate, config.rewards_rate_denominator)
/// Return the reward rate of this epoch.
public fun get_reward_rate(config: &StakingConfig): (u64, u64) acquires StakingRewardsConfig {
let (rewards_rate, rewards_rate_denominator) = if (features::periodical_reward_rate_decrease_enabled()) {
let epoch_rewards_rate = borrow_global<StakingRewardsConfig>(@aptos_framework).rewards_rate;
if (fixed_point64::is_zero(epoch_rewards_rate)) {
(0u64, 1u64)
} else {
// Maximize denominator for higher precision.
// Restriction: nominator <= MAX_REWARDS_RATE && denominator <= MAX_U64
let denominator = fixed_point64::divide_u128((MAX_REWARDS_RATE as u128), epoch_rewards_rate);
if (denominator > MAX_U64) {
denominator = MAX_U64
};
let nominator = (fixed_point64::multiply_u128(denominator, epoch_rewards_rate) as u64);
(nominator, (denominator as u64))
}
} else {
(config.rewards_rate, config.rewards_rate_denominator)
};
(rewards_rate, rewards_rate_denominator)
}

/// Return the joining limit %.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,17 @@ spec aptos_framework::staking_config {
}

spec get_reward_rate(config: &StakingConfig): (u64, u64) {
aborts_if features::spec_reward_rate_decrease_enabled();
include StakingRewardsConfigRequirement;
}

spec calculate_and_save_latest_epoch_rewards_rate(): FixedPoint64 {
aborts_if !exists<StakingRewardsConfig>(@aptos_framework);
aborts_if !features::spec_reward_rate_decrease_enabled();
aborts_if !features::spec_periodical_reward_rate_decrease_enabled();
include StakingRewardsConfigRequirement;
}

spec calculate_and_save_latest_rewards_config(): StakingRewardsConfig {
requires features::spec_reward_rate_decrease_enabled();
requires features::spec_periodical_reward_rate_decrease_enabled();
include StakingRewardsConfigRequirement;
aborts_if !exists<StakingRewardsConfig>(@aptos_framework);
}
Expand Down Expand Up @@ -134,7 +134,7 @@ spec aptos_framework::staking_config {
new_rewards_rate_denominator: u64,
) {
use std::signer;
aborts_if features::spec_reward_rate_decrease_enabled();
aborts_if features::spec_periodical_reward_rate_decrease_enabled();
let addr = signer::address_of(aptos_framework);
aborts_if addr != @aptos_framework;
aborts_if new_rewards_rate_denominator <= 0;
Expand Down Expand Up @@ -209,7 +209,7 @@ spec aptos_framework::staking_config {

spec schema StakingRewardsConfigRequirement {
requires exists<timestamp::CurrentTimeMicroseconds>(@aptos_framework);
include features::spec_reward_rate_decrease_enabled() ==> StakingRewardsConfigEnabledRequirement;
include features::spec_periodical_reward_rate_decrease_enabled() ==> StakingRewardsConfigEnabledRequirement;
}

spec schema StakingRewardsConfigEnabledRequirement {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,10 @@ module aptos_framework::delegation_pool {

#[test_only]
use aptos_framework::reconfiguration;
#[test_only]
use aptos_std::fixed_point64;
#[test_only]
use aptos_framework::timestamp::fast_forward_seconds;

#[test_only]
const CONSENSUS_KEY_1: vector<u8> = x"8a54b92288d4ba5073d3a52e80cc00ae9fbbc1cc5b433b46089b7804c38a76f00fc64746c7685ee628fc2d0b929c2294";
Expand Down Expand Up @@ -1317,6 +1321,40 @@ module aptos_framework::delegation_pool {
// stakes should remain the same - `Self::get_stake` correctly calculates them
synchronize_delegation_pool(pool_address);
assert_delegation(delegator1_address, pool_address, 11201699216002, 0, 2000000000000);

let reward_period_start_time_in_sec = timestamp::now_seconds();
// Enable rewards rate decrease. Initially rewards rate is still 1% every epoch. Rewards rate halves every year.
let one_year_in_secs: u64 = 31536000;
staking_config::initialize_rewards(
aptos_framework,
fixed_point64::create_from_rational(2, 100),
fixed_point64::create_from_rational(6, 1000),
one_year_in_secs,
reward_period_start_time_in_sec,
fixed_point64::create_from_rational(50, 100),
);
features::change_feature_flags(aptos_framework, vector[features::get_periodical_reward_rate_decrease_feature()], vector[]);

// add more stake from delegator 1
stake::mint(delegator1, 20000 * ONE_APT);
let delegator1_pending_inactive: u64;
(delegator1_active, _, delegator1_pending_inactive) = get_stake(pool_address, delegator1_address);
fee = get_add_stake_fee(pool_address, 20000 * ONE_APT);
add_stake(delegator1, pool_address, 20000 * ONE_APT);

assert_delegation(delegator1_address, pool_address, delegator1_active + 20000 * ONE_APT - fee, 0, delegator1_pending_inactive);

// delegator 1 unlocks his entire newly added stake
unlock(delegator1, pool_address, 20000 * ONE_APT - fee);
end_aptos_epoch();
// delegator 1 should own previous 11201699216002 active * ~1.01253 and 20000 * ~1.01253 + 20000 coins pending_inactive
assert_delegation(delegator1_address, pool_address, 11342056366822, 0, 4025059974939);

// stakes should remain the same - `Self::get_stake` correctly calculates them
synchronize_delegation_pool(pool_address);
assert_delegation(delegator1_address, pool_address, 11342056366822, 0, 4025059974939);

fast_forward_seconds(one_year_in_secs);
}

#[test(aptos_framework = @aptos_framework, validator = @0x123, delegator = @0x010)]
Expand Down
Loading