Skip to content

Commit

Permalink
Compiles. Tests fail
Browse files Browse the repository at this point in the history
chore: update formatting and comments in EmissionSchedule and test files
chore: update tests for tail emission in schedule

The tests for the tail emission in the schedule have been updated to include the faucet value in the supply calculations. This ensures that the total supply up to the tail emission is correct. Additionally, new tests have been added to cover the inflating tail emission.
feat: update tail inflation calculation to use basis points

The `EmissionSchedule` struct now includes a `inflation_bips` field, which represents the tail inflation rate in basis points (bips). The `new_tail_emission` method in the `Emission` implementation has been updated to calculate the `epoch_issuance` using the updated inflation rate. Additionally, the `as_u128` method has been added to convert the `MicroMinotari` value to a `u128` for more precise calculations. 

In the `consensus` module, the `inflation_bips` field in the `NetworkParams` struct has been updated to use basis points. The inflation rates for different networks have been adjusted accordingly.
  • Loading branch information
CjS77 committed Feb 21, 2024
1 parent f5860a8 commit 9de36db
Show file tree
Hide file tree
Showing 12 changed files with 319 additions and 234 deletions.
4 changes: 3 additions & 1 deletion applications/minotari_app_grpc/proto/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ message ConsensusConstants {
uint64 median_timestamp_count = 9;
uint64 emission_initial = 10;
repeated uint64 emission_decay = 11;
uint64 emission_tail = 12;
uint64 emission_tail = 12 [deprecated=true];
uint64 min_sha3x_pow_difficulty = 13;
uint64 block_weight_inputs = 14;
uint64 block_weight_outputs = 15;
Expand All @@ -141,4 +141,6 @@ message ConsensusConstants {
uint64 validator_node_registration_min_lock_height = 32;
uint64 validator_node_registration_shuffle_interval_epoch = 33;
repeated PermittedRangeProofs permitted_range_proof_types = 34;
uint64 inflation_bips = 35;
uint64 tail_epoch_length = 36;
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::tari_rpc as grpc;
impl From<ConsensusConstants> for grpc::ConsensusConstants {
#[allow(clippy::too_many_lines)]
fn from(cc: ConsensusConstants) -> Self {
let (emission_initial, emission_decay, emission_tail) = cc.emission_amounts();
let (emission_initial, emission_decay, inflation_bips, tail_epoch_length) = cc.emission_amounts();
let weight_params = cc.transaction_weight_params().params();
let input_version_range = cc.input_version_range().clone().into_inner();
let input_version_range = grpc::Range {
Expand Down Expand Up @@ -110,7 +110,9 @@ impl From<ConsensusConstants> for grpc::ConsensusConstants {
median_timestamp_count: u64::try_from(cc.median_timestamp_count()).unwrap_or(0),
emission_initial: emission_initial.into(),
emission_decay: emission_decay.to_vec(),
emission_tail: emission_tail.into(),
emission_tail: 0,
inflation_bips,
tail_epoch_length,
min_sha3x_pow_difficulty: cc.min_pow_difficulty(PowAlgorithm::Sha3x).into(),
block_weight_inputs: weight_params.input_weight,
block_weight_outputs: weight_params.output_weight,
Expand Down
149 changes: 88 additions & 61 deletions base_layer/core/src/consensus/consensus_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use crate::{
consensus::network::NetworkConsensus,
proof_of_work::{Difficulty, PowAlgorithm},
transactions::{
tari_amount::{uT, MicroMinotari, T},
tari_amount::{uT, MicroMinotari},
transaction_components::{
OutputFeatures,
OutputFeaturesVersion,
Expand All @@ -50,6 +50,8 @@ use crate::{
},
};

const ANNUAL_BLOCKS: u64 = 30 /* blocks/hr */ * 24 /* hr /d */ * 366 /* days / yr */;

/// This is the inner struct used to control all consensus values.
#[derive(Debug, Clone)]
pub struct ConsensusConstants {
Expand Down Expand Up @@ -77,8 +79,10 @@ pub struct ConsensusConstants {
/// This is the emission curve decay factor as a sum of fraction powers of two. e.g. [1,2] would be 1/2 + 1/4. [2]
/// would be 1/4
pub(in crate::consensus) emission_decay: &'static [u64],
/// This is the emission curve tail amount
pub(in crate::consensus) emission_tail: MicroMinotari,
/// The tail emission inflation rate in basis points (bips). 100 bips = 1 percentage_point
pub(in crate::consensus) inflation_bips: u64,
/// The length, in blocks of each tail emission epoch (where the reward is held constant)
pub(in crate::consensus) tail_epoch_length: u64,
/// This is the maximum age a Monero merge mined seed can be reused
/// Monero forces a change every height mod 2048 blocks
max_randomx_seed_height: u64,
Expand Down Expand Up @@ -165,9 +169,14 @@ impl ConsensusConstants {
self.effective_from_height
}

/// This gets the emission curve values as (initial, decay, tail)
pub fn emission_amounts(&self) -> (MicroMinotari, &'static [u64], MicroMinotari) {
(self.emission_initial, self.emission_decay, self.emission_tail)
/// This gets the emission curve values as (initial, decay, inflation_bips, epoch_length)
pub fn emission_amounts(&self) -> (MicroMinotari, &'static [u64], u64, u64) {
(
self.emission_initial,
self.emission_decay,
self.inflation_bips,
self.tail_epoch_length,
)
}

/// The min height maturity a coinbase utxo must have.
Expand Down Expand Up @@ -380,7 +389,8 @@ impl ConsensusConstants {
median_timestamp_count: 11,
emission_initial: 18_462_816_327 * uT,
emission_decay: &ESMERALDA_DECAY_PARAMS,
emission_tail: 800 * T,
inflation_bips: 1000,
tail_epoch_length: 100,
max_randomx_seed_height: u64::MAX,
max_extra_field_size: 200,
proof_of_work: algos,
Expand Down Expand Up @@ -443,7 +453,8 @@ impl ConsensusConstants {
median_timestamp_count: 11,
emission_initial: 5_538_846_115 * uT,
emission_decay: &EMISSION_DECAY,
emission_tail: 100.into(),
inflation_bips: 100,
tail_epoch_length: ANNUAL_BLOCKS,
max_randomx_seed_height: u64::MAX,
max_extra_field_size: 200,
proof_of_work: algos,
Expand Down Expand Up @@ -499,7 +510,8 @@ impl ConsensusConstants {
median_timestamp_count: 11,
emission_initial: ESMERALDA_INITIAL_EMISSION,
emission_decay: &ESMERALDA_DECAY_PARAMS,
emission_tail: 800 * T,
inflation_bips: 100,
tail_epoch_length: ANNUAL_BLOCKS,
max_randomx_seed_height: 3000,
max_extra_field_size: 200,
proof_of_work: algos,
Expand Down Expand Up @@ -554,7 +566,8 @@ impl ConsensusConstants {
median_timestamp_count: 11,
emission_initial: INITIAL_EMISSION,
emission_decay: &EMISSION_DECAY,
emission_tail: 800 * T,
inflation_bips: 100,
tail_epoch_length: ANNUAL_BLOCKS,
max_randomx_seed_height: 3000,
max_extra_field_size: 200,
proof_of_work: algos,
Expand Down Expand Up @@ -603,7 +616,8 @@ impl ConsensusConstants {
median_timestamp_count: 11,
emission_initial: INITIAL_EMISSION,
emission_decay: &EMISSION_DECAY,
emission_tail: 800 * T,
inflation_bips: 100,
tail_epoch_length: ANNUAL_BLOCKS,
max_randomx_seed_height: 3000,
max_extra_field_size: 200,
proof_of_work: algos,
Expand Down Expand Up @@ -654,7 +668,8 @@ impl ConsensusConstants {
median_timestamp_count: 11,
emission_initial: 10_000_000.into(),
emission_decay: &EMISSION_DECAY,
emission_tail: 100.into(),
inflation_bips: 100,
tail_epoch_length: ANNUAL_BLOCKS,
max_randomx_seed_height: u64::MAX,
max_extra_field_size: 200,
proof_of_work: algos,
Expand Down Expand Up @@ -835,11 +850,13 @@ impl ConsensusConstantsBuilder {
mut self,
intial_amount: MicroMinotari,
decay: &'static [u64],
tail_amount: MicroMinotari,
inflation_bips: u64,
epoch_length: u64,
) -> Self {
self.consensus.emission_initial = intial_amount;
self.consensus.emission_decay = decay;
self.consensus.emission_tail = tail_amount;
self.consensus.inflation_bips = inflation_bips;
self.consensus.tail_epoch_length = epoch_length;
self
}

Expand Down Expand Up @@ -868,15 +885,13 @@ impl ConsensusConstantsBuilder {

#[cfg(test)]
mod test {
use std::convert::TryFrom;

use crate::{
consensus::{
emission::{Emission, EmissionSchedule},
ConsensusConstants,
},
transactions::{
tari_amount::{uT, MicroMinotari},
tari_amount::{uT, MicroMinotari, T},
transaction_components::{OutputType, RangeProofType},
},
};
Expand Down Expand Up @@ -940,33 +955,41 @@ mod test {
let schedule = EmissionSchedule::new(
esmeralda[0].emission_initial,
esmeralda[0].emission_decay,
esmeralda[0].emission_tail,
esmeralda[0].inflation_bips,
esmeralda[0].tail_epoch_length,
esmeralda[0].faucet_value(),
);
// No genesis block coinbase
assert_eq!(schedule.block_reward(0), MicroMinotari(0));
// Coinbases starts at block 1
let coinbase_offset = 1;
let first_reward = schedule.block_reward(coinbase_offset);
assert_eq!(first_reward, esmeralda[0].emission_initial * uT);
assert_eq!(schedule.supply_at_block(coinbase_offset), first_reward);
assert_eq!(first_reward, esmeralda[0].emission_initial);
assert_eq!(
schedule.supply_at_block(coinbase_offset),
first_reward + esmeralda[0].faucet_value()
);
// 'half_life_block' at approximately '(total supply - faucet value) / 2'
#[allow(clippy::cast_possible_truncation)]
let half_life_block = (365.0 * 24.0 * 30.0 * 2.76) as u64;
let half_life_block = 365 * 24 * 30 * 3;
assert_eq!(
schedule.supply_at_block(half_life_block + coinbase_offset),
7_483_280_506_356_578 * uT
7_935_818_494_624_306 * uT + esmeralda[0].faucet_value()
);
// Tail emission starts after block 3,255,552 + coinbase_offset
let mut rewards = schedule
.iter()
.skip(3255552 + usize::try_from(coinbase_offset).unwrap());
// 21 billion
let mut rewards = schedule.iter().skip(3255552 + coinbase_offset as usize);
let (block_num, reward, supply) = rewards.next().unwrap();
assert_eq!(block_num, 3255553 + coinbase_offset);
assert_eq!(reward, 800_000_415 * uT);
let total_supply_up_to_tail_emission = supply + esmeralda[0].faucet_value;
assert_eq!(total_supply_up_to_tail_emission, 20_999_999_999_819_869 * uT);
assert_eq!(supply, 20_999_999_999_819_869 * uT);
let (_, reward, _) = rewards.next().unwrap();
assert_eq!(reward, esmeralda[0].emission_tail);
assert_eq!(reward, 799_999_715 * uT);
// Inflating tail emission
let mut rewards = schedule.iter().skip(3259845);
let (block_num, reward, supply) = rewards.next().unwrap();
assert_eq!(block_num, 3259846);
assert_eq!(reward, 797 * T);
assert_eq!(supply, 21_003_427_156_818_122 * uT);
}

#[test]
Expand All @@ -975,33 +998,33 @@ mod test {
let schedule = EmissionSchedule::new(
nextnet[0].emission_initial,
nextnet[0].emission_decay,
nextnet[0].emission_tail,
nextnet[0].inflation_bips,
nextnet[0].tail_epoch_length,
nextnet[0].faucet_value(),
);
// No genesis block coinbase
assert_eq!(schedule.block_reward(0), MicroMinotari(0));
// Coinbases starts at block 1
let coinbase_offset = 1;
let first_reward = schedule.block_reward(coinbase_offset);
assert_eq!(first_reward, nextnet[0].emission_initial * uT);
assert_eq!(schedule.supply_at_block(coinbase_offset), first_reward);
assert_eq!(
schedule.supply_at_block(coinbase_offset),
first_reward + nextnet[0].faucet_value()
);
// 'half_life_block' at approximately '(total supply - faucet value) / 2'
#[allow(clippy::cast_possible_truncation)]
let half_life_block = (365.0 * 24.0 * 30.0 * 2.76) as u64;
assert_eq!(
schedule.supply_at_block(half_life_block + coinbase_offset),
7_483_280_506_356_578 * uT
7_483_280_506_356_578 * uT + nextnet[0].faucet_value()
);
// Tail emission starts after block 3,255,552 + coinbase_offset
let mut rewards = schedule
.iter()
.skip(3255552 + usize::try_from(coinbase_offset).unwrap());
// Tail emission
let mut rewards = schedule.iter().skip(3259845);
let (block_num, reward, supply) = rewards.next().unwrap();
assert_eq!(block_num, 3255553 + coinbase_offset);
assert_eq!(reward, 800_000_415 * uT);
let total_supply_up_to_tail_emission = supply + nextnet[0].faucet_value;
assert_eq!(total_supply_up_to_tail_emission, 20_999_999_999_819_869 * uT);
let (_, reward, _) = rewards.next().unwrap();
assert_eq!(reward, nextnet[0].emission_tail);
assert_eq!(block_num, 3259846);
assert_eq!(reward, 797 * T);
assert_eq!(supply, 21_003_427_156_818_122 * uT);
}

#[test]
Expand All @@ -1010,39 +1033,45 @@ mod test {
let schedule = EmissionSchedule::new(
stagenet[0].emission_initial,
stagenet[0].emission_decay,
stagenet[0].emission_tail,
stagenet[0].inflation_bips,
stagenet[0].tail_epoch_length,
stagenet[0].faucet_value(),
);
// No genesis block coinbase
assert_eq!(schedule.block_reward(0), MicroMinotari(0));
// Coinbases starts at block 1
let coinbase_offset = 1;
let first_reward = schedule.block_reward(coinbase_offset);
assert_eq!(first_reward, stagenet[0].emission_initial * uT);
assert_eq!(schedule.supply_at_block(coinbase_offset), first_reward);
assert_eq!(
schedule.supply_at_block(coinbase_offset),
first_reward + stagenet[0].faucet_value()
);
// 'half_life_block' at approximately '(total supply - faucet value) / 2'
#[allow(clippy::cast_possible_truncation)]
let half_life_block = (365.0 * 24.0 * 30.0 * 2.76) as u64;
assert_eq!(
schedule.supply_at_block(half_life_block + coinbase_offset),
7_483_280_506_356_578 * uT
7_483_280_506_356_578 * uT + stagenet[0].faucet_value()
);
// Tail emission starts after block 3,255,552 + coinbase_offset
let mut rewards = schedule
.iter()
.skip(3255552 + usize::try_from(coinbase_offset).unwrap());
// Tail emission
let mut rewards = schedule.iter().skip(3259845);
let (block_num, reward, supply) = rewards.next().unwrap();
assert_eq!(block_num, 3255553 + coinbase_offset);
assert_eq!(reward, 800_000_415 * uT);
let total_supply_up_to_tail_emission = supply + stagenet[0].faucet_value;
assert_eq!(total_supply_up_to_tail_emission, 20_999_999_999_819_869 * uT);
let (_, reward, _) = rewards.next().unwrap();
assert_eq!(reward, stagenet[0].emission_tail);
assert_eq!(block_num, 3259846);
assert_eq!(reward, 797 * T);
assert_eq!(supply, 21_003_427_156_818_122 * uT);
}

#[test]
fn igor_schedule() {
let igor = ConsensusConstants::igor();
let schedule = EmissionSchedule::new(igor[0].emission_initial, igor[0].emission_decay, igor[0].emission_tail);
let schedule = EmissionSchedule::new(
igor[0].emission_initial,
igor[0].emission_decay,
igor[0].inflation_bips,
igor[0].tail_epoch_length,
igor[0].faucet_value(),
);
// No genesis block coinbase
assert_eq!(schedule.block_reward(0), MicroMinotari(0));
// Coinbases starts at block 1
Expand All @@ -1055,11 +1084,9 @@ mod test {
let mut previous_reward = MicroMinotari(0);
for (block_num, reward, supply) in rewards {
if reward == previous_reward {
assert_eq!(block_num, 11_084_819 + 1);
assert_eq!(supply, MicroMinotari(6_326_198_792_915_738));
// These set of constants does not result in a tail emission equal to the specified tail emission
assert_ne!(reward, igor[0].emission_tail);
assert_eq!(reward, MicroMinotari(2_097_151));
assert_eq!(block_num, 11_084_796);
assert_eq!(supply, MicroMinotari(8_010_884_615_082_026));
assert_eq!(reward, MicroMinotari(303_000_000));
break;
}
previous_reward = reward;
Expand Down
9 changes: 5 additions & 4 deletions base_layer/core/src/consensus/consensus_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ impl ConsensusManager {
}
}

/// Get a pointer to the emission schedule
/// The height provided here, decides the emission curve to use. It swaps to the integer curve upon reaching
/// 1_000_000_000
/// Get a reference to the emission parameters
pub fn emission_schedule(&self) -> &EmissionSchedule {
&self.inner.emission
}
Expand Down Expand Up @@ -241,8 +239,11 @@ impl ConsensusManagerBuilder {
let emission = EmissionSchedule::new(
self.consensus_constants[0].emission_initial,
self.consensus_constants[0].emission_decay,
self.consensus_constants[0].emission_tail,
self.consensus_constants[0].inflation_bips,
self.consensus_constants[0].tail_epoch_length,
self.consensus_constants[0].faucet_value(),
);

let inner = ConsensusManagerInner {
consensus_constants: self.consensus_constants,
network: self.network,
Expand Down
Loading

0 comments on commit 9de36db

Please sign in to comment.