From 63b1f6864ef080f9eef9ba9d6a600ab86c8791c5 Mon Sep 17 00:00:00 2001 From: Cayle Sharrock Date: Wed, 28 Feb 2024 13:42:16 +0000 Subject: [PATCH] feat!: implement inflating tail emission (#6160) ## Summary Adds a feature `tari_feature_mainnet_emission` to allow for tail emission inflation. See #6122 This change necessitates the addition of 2 new consensus constants: `inflation_bips` -- the annual inflation rate of the total supply. and `tail_emission_epoch_length`, which controls the tail emission inflation. These replace `tail_emission`. We update the Protobuf definition for `ConsensusConstants` to account for the new fields. Note: Replaces part of #6131 --- .../minotari_app_grpc/proto/types.proto | 4 +- .../src/conversions/consensus_constants.rs | 7 +- .../core/src/consensus/consensus_constants.rs | 143 ++++---- .../core/src/consensus/consensus_manager.rs | 9 +- base_layer/core/src/consensus/emission.rs | 314 +++++++++++------- .../core/src/transactions/tari_amount.rs | 5 + .../core/src/validation/chain_balance.rs | 5 +- .../core/tests/helpers/sample_blockchains.rs | 16 +- base_layer/core/tests/helpers/sync.rs | 9 +- base_layer/core/tests/tests/mempool.rs | 14 +- base_layer/core/tests/tests/node_service.rs | 15 +- .../core/tests/tests/node_state_machine.rs | 11 +- 12 files changed, 323 insertions(+), 229 deletions(-) diff --git a/applications/minotari_app_grpc/proto/types.proto b/applications/minotari_app_grpc/proto/types.proto index 474e2bc3ab..c652dbd6bf 100644 --- a/applications/minotari_app_grpc/proto/types.proto +++ b/applications/minotari_app_grpc/proto/types.proto @@ -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; @@ -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; } diff --git a/applications/minotari_app_grpc/src/conversions/consensus_constants.rs b/applications/minotari_app_grpc/src/conversions/consensus_constants.rs index 80fac7416e..74818c8cb0 100644 --- a/applications/minotari_app_grpc/src/conversions/consensus_constants.rs +++ b/applications/minotari_app_grpc/src/conversions/consensus_constants.rs @@ -29,7 +29,7 @@ use crate::tari_rpc as grpc; impl From 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 { @@ -100,6 +100,7 @@ impl From for grpc::ConsensusConstants { let proof_of_work = HashMap::from_iter([(0u32, randomx_pow), (1u32, sha3x_pow)]); + #[allow(deprecated)] Self { coinbase_min_maturity: cc.coinbase_min_maturity(), blockchain_version: cc.blockchain_version().into(), @@ -110,7 +111,9 @@ impl From 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, diff --git a/base_layer/core/src/consensus/consensus_constants.rs b/base_layer/core/src/consensus/consensus_constants.rs index d5729296ac..92d65df621 100644 --- a/base_layer/core/src/consensus/consensus_constants.rs +++ b/base_layer/core/src/consensus/consensus_constants.rs @@ -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, @@ -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 { @@ -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, @@ -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. @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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 } @@ -876,7 +893,7 @@ mod test { ConsensusConstants, }, transactions::{ - tari_amount::{uT, MicroMinotari}, + tari_amount::{uT, MicroMinotari, T}, transaction_components::{OutputType, RangeProofType}, }, }; @@ -940,33 +957,43 @@ 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 + // 21 billion let mut rewards = schedule .iter() .skip(3255552 + usize::try_from(coinbase_offset).unwrap()); 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] @@ -975,7 +1002,9 @@ 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)); @@ -983,25 +1012,23 @@ mod test { 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] @@ -1010,7 +1037,9 @@ 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)); @@ -1018,31 +1047,35 @@ mod test { 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 @@ -1055,11 +1088,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; diff --git a/base_layer/core/src/consensus/consensus_manager.rs b/base_layer/core/src/consensus/consensus_manager.rs index e315848c9f..4bb4c0814a 100644 --- a/base_layer/core/src/consensus/consensus_manager.rs +++ b/base_layer/core/src/consensus/consensus_manager.rs @@ -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 } @@ -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, diff --git a/base_layer/core/src/consensus/emission.rs b/base_layer/core/src/consensus/emission.rs index d6cfc671bc..09b7237f0b 100644 --- a/base_layer/core/src/consensus/emission.rs +++ b/base_layer/core/src/consensus/emission.rs @@ -20,8 +20,6 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::cmp; - use crate::transactions::tari_amount::MicroMinotari; pub trait Emission { @@ -29,32 +27,50 @@ pub trait Emission { fn supply_at_block(&self, height: u64) -> MicroMinotari; } -/// The Minotari emission schedule. The emission schedule determines how much Minotari is mined as a block reward at -/// every block. -/// -/// NB: We don't know what the final emission schedule will be on Minotari yet, so do not give any weight to values or -/// formulae provided in this file, they will almost certainly change ahead of main-net release. +/// The Minotari emission schedule with inflating tail emission. The emission schedule determines how much Minotari is +/// mined as a block reward at every block. #[derive(Debug, Clone)] pub struct EmissionSchedule { initial: MicroMinotari, decay: &'static [u64], - tail: MicroMinotari, + inflation_bips: u64, // Tail inflation in basis points. 100 bips = 1 percentage point + epoch_length: u64, // The number of blocks in an inflation epoch + initial_supply: MicroMinotari, // The supply at block 0, from faucets or premine } impl EmissionSchedule { /// Create a new emission schedule instance. /// - /// The Emission schedule follows a similar pattern to Monero; with an exponentially decaying emission rate with - /// a constant tail emission rate. + /// ## Primary emission schedule + /// + /// The Emission schedule follows a similar pattern to Monero; with an initially exponentially decaying emission + /// rate and a tail emission. + /// /// - /// The block reward is given by - /// $$ r_n = \mathrm{MAX}(\mathrm(intfloor(r_{n-1} * (1 - \epsilon)), t) n > 0 $$ + /// The decay portion is given by + /// $$ r_n = \lfloor r_{n-1} * (1 - \epsilon) \rfloor, n > 0 $$ /// $$ r_0 = A_0 $$ /// /// where /// * $$A_0$$ is the genesis block reward /// * $$1 - \epsilon$$ is the decay rate - /// * $$t$$ is the constant tail emission rate + /// + /// The decay parameters are determined as described in [#decay_parameters]. + /// + /// ## Tail emission + /// + /// If the feature `mainnet_emission` is not enabled, the tail emission is constant. It is triggered if the reward + /// would fall below the `tail` value. + /// + /// If the feature `mainnet_emission` is enabled, the tail emission is calculated as follows: + /// + /// At each block, the reward is multiplied by `EPOCH_LENGTH` (approximately a year's worth of blocks) to + /// calculate `annual_supply`. + /// If `annual_supply/current_supply` is less than `0.01*inflation_bips`% then we enter tail emission mode. + /// + /// Every `EPOCH_LENGTH` blocks, the inflation rate is recalculated based on the current supply. + /// + /// ## Decay parameters /// /// The `intfloor` function is an integer-math-based multiplication of an integer by a fraction that's very close /// to one (e.g. 0.998,987,123,432)` that @@ -97,12 +113,24 @@ impl EmissionSchedule { /// /// The shift right operation will overflow if shifting more than 63 bits. `new` will panic if any of the decay /// values are greater than or equal to 64. - pub fn new(initial: MicroMinotari, decay: &'static [u64], tail: MicroMinotari) -> EmissionSchedule { + pub fn new( + initial: MicroMinotari, + decay: &'static [u64], + inflation_bips: u64, + epoch_length: u64, + initial_supply: MicroMinotari, + ) -> EmissionSchedule { assert!( decay.iter().all(|i| *i < 64), "Decay value would overflow. All `decay` values must be less than 64" ); - EmissionSchedule { initial, decay, tail } + EmissionSchedule { + initial, + decay, + inflation_bips, + epoch_length, + initial_supply, + } } /// Utility function to calculate the decay parameters that are provided in [EmissionSchedule::new]. This function @@ -113,7 +141,7 @@ impl EmissionSchedule { /// /// Input : `k`: A string representing a floating point number of (nearly) arbitrary precision, and less than one. /// - /// Returns: An array of powers of negative two when when applied as a shift right and sum operation is very + /// Returns: An array of powers of negative two when applied as a shift right and sum operation is very /// close to (1-k)*n. /// /// None - If k is not a valid floating point number less than one. @@ -173,18 +201,6 @@ impl EmissionSchedule { /// the emission curve if you're interested in the supply as well as the reward. /// /// This is an infinite iterator, and each value returned is a tuple of (block number, reward, and total supply) - /// - /// ```edition2018 - /// use tari_core::{ - /// consensus::emission::EmissionSchedule, - /// transactions::tari_amount::MicroMinotari, - /// }; - /// // Print the reward and supply for first 100 blocks - /// let schedule = EmissionSchedule::new(10.into(), &[3], 1.into()); - /// for (n, reward, supply) in schedule.iter().take(100) { - /// println!("{:3} {:9} {:9}", n, reward, supply); - /// } - /// ``` pub fn iter(&self) -> EmissionRate { EmissionRate::new(self) } @@ -203,15 +219,19 @@ pub struct EmissionRate<'a> { supply: MicroMinotari, reward: MicroMinotari, schedule: &'a EmissionSchedule, + epoch: u64, + epoch_counter: u64, } impl<'a> EmissionRate<'a> { fn new(schedule: &'a EmissionSchedule) -> EmissionRate<'a> { EmissionRate { block_num: 0, - supply: MicroMinotari(0), + supply: schedule.initial_supply, reward: MicroMinotari(0), schedule, + epoch: 0, + epoch_counter: 0, } } @@ -227,21 +247,53 @@ impl<'a> EmissionRate<'a> { self.reward } + fn next_decay_reward(&self) -> MicroMinotari { + let r = self.reward.as_u64(); + self.schedule + .decay + .iter() + .fold(self.reward, |sum, i| sum - MicroMinotari::from(r >> *i)) + } + /// Calculates the next reward by multiplying the decay factor by the previous block reward using integer math. /// /// We write the decay factor, 1 - k, as a sum of fraction powers of two. e.g. if we wanted 0.25 as our k, then /// (1-k) would be 0.75 = 1/2 plus 1/4 (1/2^2). /// /// Then we calculate k.R = (1 - e).R = R - e.R = R - (0.5 * R + 0.25 * R) = R - R >> 1 - R >> 2 - fn next_reward(&self) -> MicroMinotari { - let r = self.reward.as_u64(); - let next = self - .schedule - .decay - .iter() - .fold(self.reward, |sum, i| sum - MicroMinotari::from(r >> *i)); + fn next_reward(&mut self) { + // Inflation phase + if self.epoch > 0 { + self.epoch_counter += 1; + if self.epoch_counter >= self.schedule.epoch_length { + self.epoch_counter = 0; + self.epoch += 1; + self.reward = self.new_tail_emission(); + } + } else { + // Decay phase + let cutoff = self.new_tail_emission(); + let next_decay_reward = self.next_decay_reward(); + if self.epoch == 0 && next_decay_reward > cutoff { + self.reward = next_decay_reward; + } else { + self.epoch = 1; + self.reward = cutoff; + } + } + } - cmp::max(next, self.schedule.tail) + fn new_tail_emission(&self) -> MicroMinotari { + // Remember: 100% = 10,000 bips + let epoch_issuance = self + .supply + .as_u128() + .saturating_mul(u128::from(self.schedule.inflation_bips)) / + 10_000u128; + #[allow(clippy::cast_possible_truncation)] + let epoch_issuance = epoch_issuance as u64; // intentionally allow rounding via truncation + let reward = epoch_issuance / self.schedule.epoch_length; // in uT + MicroMinotari::from((reward / 1_000_000) * 1_000_000) // truncate to nearest whole XTR } } @@ -252,11 +304,11 @@ impl Iterator for EmissionRate<'_> { self.block_num += 1; if self.block_num == 1 { self.reward = self.schedule.initial; - self.supply = self.schedule.initial; + self.supply = self.supply.checked_add(self.reward)?; return Some((self.block_num, self.reward, self.supply)); } - self.reward = self.next_reward(); - // Once we've reached max supply, the iterator is done + self.next_reward(); // Has side effect + // Once we've reached max supply, the iterator is done self.supply = self.supply.checked_add(self.reward)?; Some((self.block_num, self.reward, self.supply)) } @@ -280,25 +332,90 @@ impl Emission for EmissionSchedule { #[cfg(test)] mod test { + #[test] + fn calc_array() { + assert_eq!(EmissionSchedule::decay_params("1.00"), None); + assert_eq!(EmissionSchedule::decay_params("56345"), None); + assert_eq!(EmissionSchedule::decay_params("0.75").unwrap(), vec![2]); + assert_eq!(EmissionSchedule::decay_params("0.25").unwrap(), vec![1, 2]); + assert_eq!(EmissionSchedule::decay_params("0.5").unwrap(), vec![1]); + assert_eq!(EmissionSchedule::decay_params("0.875").unwrap(), vec![3]); + assert_eq!(EmissionSchedule::decay_params("0.125").unwrap(), vec![1, 2, 3]); + assert_eq!(EmissionSchedule::decay_params("0.64732").unwrap(), vec![ + 2, 4, 5, 7, 10, 13, 16, 19, 20, 21, 22, 25, 29, 32, 33, 34, 35, 36, 38, 45, 47, 51, 53, 58, 59, 60, 62, 63 + ]); + assert_eq!(EmissionSchedule::decay_params("0.9999991208182701").unwrap(), vec![ + 21, 22, 23, 25, 26, 37, 38, 39, 41, 45, 49, 50, 51, 52, 55, 57, 59, 60, 63 + ]); + assert_eq!(EmissionSchedule::decay_params("0.0").unwrap(), vec![0]); + } + use crate::{ consensus::emission::{Emission, EmissionSchedule}, - transactions::tari_amount::{uT, MicroMinotari, T}, + transactions::tari_amount::{MicroMinotari, T}, }; #[test] - fn schedule() { - let schedule = EmissionSchedule::new( - MicroMinotari::from(10_000_100), - &[22, 23, 24, 26, 27], - MicroMinotari::from(100), - ); - assert_eq!(schedule.block_reward(0), MicroMinotari::from(0)); - assert_eq!(schedule.supply_at_block(0), MicroMinotari::from(0)); - assert_eq!(schedule.block_reward(1), MicroMinotari::from(10_000_100)); - assert_eq!(schedule.supply_at_block(1), MicroMinotari::from(10_000_100)); - // These values have been independently calculated - assert_eq!(schedule.block_reward(100 + 1), MicroMinotari::from(9_999_800)); - assert_eq!(schedule.supply_at_block(100 + 1), MicroMinotari::from(1_009_994_950)); + #[allow(clippy::cast_possible_truncation)] + fn mainnet_emission() { + let epoch_length = 30 * 24 * 366; + let halflife = 3 * 30 * 24 * 365; + let a0 = MicroMinotari::from(12_923_971_428); + let decay = &[21u64, 22, 23, 25, 26, 37, 38, 40]; + let premine = 6_300_000_000 * T; + let schedule = EmissionSchedule::new(a0, decay, 100, epoch_length, premine); + let mut iter = schedule.iter(); + assert_eq!(iter.block_num, 0); + assert_eq!(iter.reward, MicroMinotari::from(0)); + assert_eq!(iter.supply, premine); + let (num, reward, supply) = iter.next().unwrap(); + // Block 1 + assert_eq!(num, 1); + assert_eq!(reward, MicroMinotari::from(12_923_971_428)); + assert_eq!(supply, MicroMinotari::from(6_300_012_923_971_428)); + // Block 2 + let (num, reward, supply) = iter.next().unwrap(); + assert_eq!(num, 2); + assert_eq!(reward, MicroMinotari::from(12_923_960_068)); + assert_eq!(supply, MicroMinotari::from(6_300_025_847_931_496)); + + // Block 788,400. 50% Mined + let mut iter = iter.skip_while(|(num, _, _)| *num < halflife); + let (num, reward, supply) = iter.next().unwrap(); + assert_eq!(num, halflife); + assert_eq!(reward.as_u64(), 6_463_480_936); + let total_supply = 21_000_000_000 * T - premine; + let residual = (supply - premine) * 2 - total_supply; + // Within 0.01% of mining half the total supply + assert!(residual < total_supply / 10000, "Residual: {}", residual); + // Head to tail emission + let mut iter = iter.skip_while(|(num, _, _)| *num < 3_220_980); + let (num, reward, supply) = iter.next().unwrap(); + assert_eq!(num, 3_220_980); + assert_eq!(reward, MicroMinotari::from(764_000_449)); + assert_eq!(supply, MicroMinotari::from(20_140_382_328_948_420)); + let (num, reward, _) = iter.next().unwrap(); + assert_eq!(num, 3_220_981); + assert_eq!(reward, 764 * T); + let (num, reward, _) = iter.next().unwrap(); + assert_eq!(num, 3_220_982); + assert_eq!(reward, 764 * T); + // Next boosting + let mut iter = iter.skip((epoch_length - 3) as usize); + let (num, reward, supply) = iter.next().unwrap(); + assert_eq!(num, 3_484_500); + assert_eq!(reward, 764 * T); + assert_eq!(supply, MicroMinotari::from(20_341_711_608_948_420)); + let (num, reward, _) = iter.next().unwrap(); + assert_eq!(num, 3_484_501); + assert_eq!(reward, 771 * T); + let (num, reward, supply) = iter.next().unwrap(); + assert_eq!(num, 3_484_502); + assert_eq!(reward, 771 * T); + // Check supply inflation. Because of rounding, it could be between 98 and 100 bips + let epoch_supply = 771 * T * epoch_length; + let inflation = (10000 * epoch_supply / supply).as_u64(); // 1 bip => 100 + assert!(inflation < 100 && inflation > 98, "Inflation: {} bips", inflation); } #[test] @@ -308,7 +425,9 @@ mod test { let schedule = EmissionSchedule::new( MicroMinotari::from(10000000u64), &[22, 23, 24, 26, 27], - MicroMinotari::from(100), + 0, + 100000, + MicroMinotari::from(0), ); // Slow but does not overflow assert_eq!(schedule.block_reward(height + 1), MicroMinotari::from(4_194_303)); @@ -320,83 +439,36 @@ mod test { let schedule = EmissionSchedule::new( MicroMinotari::from(INITIAL), &[2], // 0.25 decay - MicroMinotari::from(100), + 1000, + 10, + 100 * T, ); assert_eq!(schedule.block_reward(0), MicroMinotari(0)); - assert_eq!(schedule.supply_at_block(0), MicroMinotari(0)); + assert_eq!(schedule.supply_at_block(0), 100 * T); let values = schedule.iter().take(101).collect::>(); let (height, reward, supply) = values[0]; assert_eq!(height, 1); assert_eq!(reward, MicroMinotari::from(INITIAL)); - assert_eq!(supply, MicroMinotari::from(INITIAL)); + assert_eq!(supply, MicroMinotari::from(INITIAL) + 100 * T); let (height, reward, supply) = values[1]; assert_eq!(height, 2); assert_eq!(reward, MicroMinotari::from(7_500_075)); - assert_eq!(supply, MicroMinotari::from(17_500_175)); + assert_eq!(supply, MicroMinotari::from(117_500_175)); let (height, reward, supply) = values[2]; assert_eq!(height, 3); assert_eq!(reward, MicroMinotari::from(5_625_057)); - assert_eq!(supply, MicroMinotari::from(23_125_232)); - let (height, reward, supply) = values[10]; - assert_eq!(height, 11); - assert_eq!(reward, MicroMinotari::from(563_142)); - assert_eq!(supply, MicroMinotari::from(38_310_986)); - let (height, reward, supply) = values[41]; - assert_eq!(height, 42); - assert_eq!(reward, MicroMinotari::from(100)); - assert_eq!(supply, MicroMinotari::from(40_000_252)); - - let mut tot_supply = MicroMinotari::from(0); - for (_, reward, supply) in schedule.iter().take(1000) { - tot_supply += reward; - assert_eq!(tot_supply, supply); - } - } - - #[test] - #[allow(clippy::identity_op)] - fn emission() { - let emission = EmissionSchedule::new(1 * T, &[1, 2], 100 * uT); - let mut emission = emission.iter(); - // decay is 1 - 0.25 - 0.125 = 0.625 - assert_eq!(emission.block_height(), 0); - assert_eq!(emission.block_reward(), MicroMinotari(0)); - assert_eq!(emission.supply(), MicroMinotari(0)); - - assert_eq!(emission.next(), Some((1, 1_000_000 * uT, 1_000_000 * uT))); - assert_eq!(emission.next(), Some((2, 250_000 * uT, 1_250_000 * uT))); - assert_eq!(emission.next(), Some((3, 62_500 * uT, 1_312_500 * uT))); - assert_eq!(emission.next(), Some((4, 15_625 * uT, 1_328_125 * uT))); - assert_eq!(emission.next(), Some((5, 3_907 * uT, 1_332_032 * uT))); - assert_eq!(emission.next(), Some((6, 978 * uT, 1_333_010 * uT))); - assert_eq!(emission.next(), Some((7, 245 * uT, 1_333_255 * uT))); - // Tail emission kicks in - assert_eq!(emission.next(), Some((8, 100 * uT, 1_333_355 * uT))); - assert_eq!(emission.next(), Some((9, 100 * uT, 1_333_455 * uT))); - - assert_eq!(emission.block_height(), 9); - assert_eq!(emission.block_reward(), 100 * uT); - assert_eq!(emission.supply(), 1333455 * uT); - let schedule = EmissionSchedule::new(1 * T, &[1, 2], 100 * uT); - assert_eq!(emission.block_reward(), schedule.block_reward(9)); - assert_eq!(emission.supply(), schedule.supply_at_block(9)) - } - - #[test] - fn calc_array() { - assert_eq!(EmissionSchedule::decay_params("1.00"), None); - assert_eq!(EmissionSchedule::decay_params("56345"), None); - assert_eq!(EmissionSchedule::decay_params("0.75").unwrap(), vec![2]); - assert_eq!(EmissionSchedule::decay_params("0.25").unwrap(), vec![1, 2]); - assert_eq!(EmissionSchedule::decay_params("0.5").unwrap(), vec![1]); - assert_eq!(EmissionSchedule::decay_params("0.875").unwrap(), vec![3]); - assert_eq!(EmissionSchedule::decay_params("0.125").unwrap(), vec![1, 2, 3]); - assert_eq!(EmissionSchedule::decay_params("0.64732").unwrap(), vec![ - 2, 4, 5, 7, 10, 13, 16, 19, 20, 21, 22, 25, 29, 32, 33, 34, 35, 36, 38, 45, 47, 51, 53, 58, 59, 60, 62, 63 - ]); - assert_eq!(EmissionSchedule::decay_params("0.9999991208182701").unwrap(), vec![ - 21, 22, 23, 25, 26, 37, 38, 39, 41, 45, 49, 50, 51, 52, 55, 57, 59, 60, 63 - ]); - assert_eq!(EmissionSchedule::decay_params("0.0").unwrap(), vec![0]); + assert_eq!(supply, MicroMinotari::from(123_125_232)); + let (height, reward, supply) = values[8]; + assert_eq!(height, 9); + assert_eq!(reward, MicroMinotari::from(1_001_140)); + assert_eq!(supply, MicroMinotari::from(136_996_989)); + let (height, reward, supply) = values[9]; + assert_eq!(height, 10); + assert_eq!(reward, MicroMinotari::from(1_000_000)); + assert_eq!(supply, MicroMinotari::from(137_996_989)); + let (height, reward, supply) = values[99]; + assert_eq!(height, 100); + assert_eq!(reward, MicroMinotari::from(2_000_000)); + assert_eq!(supply, MicroMinotari::from(248_996_989)); } } diff --git a/base_layer/core/src/transactions/tari_amount.rs b/base_layer/core/src/transactions/tari_amount.rs index f679664e81..edb5e9c32f 100644 --- a/base_layer/core/src/transactions/tari_amount.rs +++ b/base_layer/core/src/transactions/tari_amount.rs @@ -148,6 +148,11 @@ impl MicroMinotari { self.0 } + #[inline] + pub fn as_u128(&self) -> u128 { + u128::from(self.0) + } + pub fn to_currency_string(&self, sep: char) -> String { format!("{} µT", format_currency(&self.as_u64().to_string(), sep)) } diff --git a/base_layer/core/src/validation/chain_balance.rs b/base_layer/core/src/validation/chain_balance.rs index ff69eb2e60..c7153d7d21 100644 --- a/base_layer/core/src/validation/chain_balance.rs +++ b/base_layer/core/src/validation/chain_balance.rs @@ -92,8 +92,9 @@ impl ChainBalanceValidator { } fn get_emission_commitment_at(&self, height: u64) -> Commitment { - let total_supply = - self.rules.get_total_emission_at(height) + self.rules.consensus_constants(height).faucet_value(); + // With inflating tail emission, we **must** know the value of the premine as part of the supply calc in order + // to determine the correct inflation curve. Therefore, the premine is already included in the supply + let total_supply = self.rules.get_total_emission_at(height); debug!( target: LOG_TARGET, "Expected emission at height {} is {}", height, total_supply diff --git a/base_layer/core/tests/helpers/sample_blockchains.rs b/base_layer/core/tests/helpers/sample_blockchains.rs index a958186ec3..40c82fb429 100644 --- a/base_layer/core/tests/helpers/sample_blockchains.rs +++ b/base_layer/core/tests/helpers/sample_blockchains.rs @@ -170,6 +170,12 @@ pub async fn create_blockchain_db_no_cut_through() -> ( (db, blocks, outputs, consensus_manager, key_manager) } +pub fn consensus_constants(network: Network) -> ConsensusConstantsBuilder { + ConsensusConstantsBuilder::new(network) + .with_emission_amounts(100_000_000.into(), &EMISSION, 10, 1000) + .with_coinbase_lockheight(1) +} + /// Create a new blockchain database containing only the Genesis block #[allow(dead_code)] pub async fn create_new_blockchain( @@ -182,10 +188,7 @@ pub async fn create_new_blockchain( MemoryDbKeyManager, ) { let key_manager = create_memory_db_key_manager(); - let consensus_constants = ConsensusConstantsBuilder::new(network) - .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) - .with_coinbase_lockheight(1) - .build(); + let consensus_constants = consensus_constants(network).build(); let (block0, output) = create_genesis_block(&consensus_constants, &key_manager).await; let consensus_manager = ConsensusManagerBuilder::new(network) .add_consensus_constants(consensus_constants) @@ -243,10 +246,7 @@ pub async fn create_new_blockchain_lmdb( MemoryDbKeyManager, ) { let key_manager = create_memory_db_key_manager(); - let consensus_constants = ConsensusConstantsBuilder::new(network) - .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) - .with_coinbase_lockheight(1) - .build(); + let consensus_constants = consensus_constants(network).build(); let (block0, output) = create_genesis_block(&consensus_constants, &key_manager).await; let consensus_manager = ConsensusManagerBuilder::new(network) .add_consensus_constants(consensus_constants) diff --git a/base_layer/core/tests/helpers/sync.rs b/base_layer/core/tests/helpers/sync.rs index a15cf7981f..93a77d1fbc 100644 --- a/base_layer/core/tests/helpers/sync.rs +++ b/base_layer/core/tests/helpers/sync.rs @@ -43,7 +43,7 @@ use tari_core::{ }, blocks::ChainBlock, chain_storage::{BlockchainDatabaseConfig, DbTransaction}, - consensus::{ConsensusConstantsBuilder, ConsensusManager, ConsensusManagerBuilder}, + consensus::{ConsensusManager, ConsensusManagerBuilder}, mempool::MempoolServiceConfig, proof_of_work::{randomx_factory::RandomXFactory, Difficulty}, test_helpers::blockchain::TempDatabase, @@ -64,10 +64,9 @@ use tokio::sync::{broadcast, watch}; use crate::helpers::{ block_builders::{append_block, create_genesis_block}, nodes::{create_network_with_multiple_base_nodes_with_config, NodeInterfaces}, + sample_blockchains, }; -static EMISSION: [u64; 2] = [10, 10]; - /// Helper function to initialize header sync with a single peer pub fn initialize_sync_headers_with_ping_pong_data( local_node_interfaces: &NodeInterfaces, @@ -152,9 +151,7 @@ pub async fn create_network_with_multiple_nodes( let network = Network::LocalNet; let temp_dir = tempdir().unwrap(); let key_manager = create_memory_db_key_manager(); - let consensus_constants = ConsensusConstantsBuilder::new(network) - .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) - .build(); + let consensus_constants = sample_blockchains::consensus_constants(network).build(); let (initial_block, coinbase_wallet_output) = create_genesis_block(&consensus_constants, &key_manager).await; let consensus_manager = ConsensusManagerBuilder::new(network) .add_consensus_constants(consensus_constants) diff --git a/base_layer/core/tests/tests/mempool.rs b/base_layer/core/tests/tests/mempool.rs index 8601ea4c95..9feb7c180f 100644 --- a/base_layer/core/tests/tests/mempool.rs +++ b/base_layer/core/tests/tests/mempool.rs @@ -1036,16 +1036,14 @@ async fn test_reorg() { mempool.process_reorg(vec![], vec![reorg_block4.into()]).await.unwrap(); } -static EMISSION: [u64; 2] = [10, 10]; #[tokio::test(flavor = "multi_thread", worker_threads = 1)] #[allow(clippy::too_many_lines)] #[allow(clippy::identity_op)] async fn receive_and_propagate_transaction() { let temp_dir = tempdir().unwrap(); let network = Network::LocalNet; - let consensus_constants = ConsensusConstantsBuilder::new(network) + let consensus_constants = crate::helpers::sample_blockchains::consensus_constants(network) .with_coinbase_lockheight(100) - .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .build(); let key_manager = create_memory_db_key_manager(); let (block0, utxo) = create_genesis_block(&consensus_constants, &key_manager).await; @@ -1171,10 +1169,8 @@ async fn receive_and_propagate_transaction() { #[allow(clippy::too_many_lines)] async fn consensus_validation_large_tx() { let network = Network::LocalNet; - // We dont want to compute the 19500 limit of local net, so we create smaller blocks - let consensus_constants = ConsensusConstantsBuilder::new(network) - .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) - .with_coinbase_lockheight(1) + // We don't want to compute the 19500 limit of local net, so we create smaller blocks + let consensus_constants = crate::helpers::sample_blockchains::consensus_constants(network) .with_max_block_transaction_weight(500) .build(); let (mut store, mut blocks, mut outputs, consensus_manager, key_manager) = @@ -1357,9 +1353,7 @@ async fn consensus_validation_large_tx() { async fn validation_reject_min_fee() { let network = Network::LocalNet; // We dont want to compute the 19500 limit of local net, so we create smaller blocks - let consensus_constants = ConsensusConstantsBuilder::new(network) - .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) - .with_coinbase_lockheight(1) + let consensus_constants = crate::helpers::sample_blockchains::consensus_constants(network) .with_max_block_transaction_weight(500) .build(); let (mut store, mut blocks, mut outputs, consensus_manager, key_manager) = diff --git a/base_layer/core/tests/tests/node_service.rs b/base_layer/core/tests/tests/node_service.rs index c03a0d7b23..cc36517d53 100644 --- a/base_layer/core/tests/tests/node_service.rs +++ b/base_layer/core/tests/tests/node_service.rs @@ -32,7 +32,7 @@ use tari_core::{ }, blocks::{ChainBlock, NewBlock}, chain_storage::BlockchainDatabaseConfig, - consensus::{ConsensusConstantsBuilder, ConsensusManager, ConsensusManagerBuilder, NetworkConsensus}, + consensus::{ConsensusManager, ConsensusManagerBuilder, NetworkConsensus}, mempool::TxStorageResponse, proof_of_work::{randomx_factory::RandomXFactory, Difficulty, PowAlgorithm}, transactions::{ @@ -88,9 +88,7 @@ async fn propagate_and_forward_many_valid_blocks() { let carol_node_identity = random_node_identity(); let dan_node_identity = random_node_identity(); let network = Network::LocalNet; - let consensus_constants = ConsensusConstantsBuilder::new(network) - .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) - .build(); + let consensus_constants = crate::helpers::sample_blockchains::consensus_constants(network).build(); let (block0, outputs) = create_genesis_block_with_utxos(&[T, T], &consensus_constants, &key_manager).await; let (tx01, _tx01_out) = spend_utxos( @@ -222,7 +220,6 @@ async fn propagate_and_forward_many_valid_blocks() { dan_node.shutdown().await; } -static EMISSION: [u64; 2] = [10, 10]; #[tokio::test(flavor = "multi_thread", worker_threads = 1)] #[allow(clippy::too_many_lines)] async fn propagate_and_forward_invalid_block_hash() { @@ -237,9 +234,7 @@ async fn propagate_and_forward_invalid_block_hash() { let carol_node_identity = random_node_identity(); let network = Network::LocalNet; let key_manager = create_memory_db_key_manager(); - let consensus_constants = ConsensusConstantsBuilder::new(network) - .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) - .build(); + let consensus_constants = crate::helpers::sample_blockchains::consensus_constants(network).build(); let (block0, genesis_coinbase) = create_genesis_block(&consensus_constants, &key_manager).await; let rules = ConsensusManager::builder(network) .add_consensus_constants(consensus_constants) @@ -370,9 +365,7 @@ async fn propagate_and_forward_invalid_block() { let dan_node_identity = random_node_identity(); let key_manager = create_memory_db_key_manager(); let network = Network::LocalNet; - let consensus_constants = ConsensusConstantsBuilder::new(network) - .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) - .build(); + let consensus_constants = crate::helpers::sample_blockchains::consensus_constants(network).build(); let (block0, _) = create_genesis_block(&consensus_constants, &key_manager).await; let rules = ConsensusManager::builder(network) .add_consensus_constants(consensus_constants) diff --git a/base_layer/core/tests/tests/node_state_machine.rs b/base_layer/core/tests/tests/node_state_machine.rs index aef35ca56c..62c2f7770c 100644 --- a/base_layer/core/tests/tests/node_state_machine.rs +++ b/base_layer/core/tests/tests/node_state_machine.rs @@ -37,7 +37,7 @@ use tari_core::{ SyncValidators, }, chain_storage::BlockchainDatabaseConfig, - consensus::{ConsensusConstantsBuilder, ConsensusManagerBuilder}, + consensus::ConsensusManagerBuilder, mempool::MempoolServiceConfig, proof_of_work::{randomx_factory::RandomXFactory, Difficulty}, test_helpers::blockchain::create_test_blockchain_db, @@ -66,15 +66,12 @@ use crate::helpers::{ }, }; -static EMISSION: [u64; 2] = [10, 10]; #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn test_listening_lagging() { let network = Network::LocalNet; let temp_dir = tempdir().unwrap(); let key_manager = create_memory_db_key_manager(); - let consensus_constants = ConsensusConstantsBuilder::new(network) - .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) - .build(); + let consensus_constants = crate::helpers::sample_blockchains::consensus_constants(network).build(); let (prev_block, _) = create_genesis_block(&consensus_constants, &key_manager).await; let consensus_manager = ConsensusManagerBuilder::new(network) .add_consensus_constants(consensus_constants) @@ -158,9 +155,7 @@ async fn test_listening_initial_fallen_behind() { let network = Network::LocalNet; let temp_dir = tempdir().unwrap(); let key_manager = create_memory_db_key_manager(); - let consensus_constants = ConsensusConstantsBuilder::new(network) - .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) - .build(); + let consensus_constants = crate::helpers::sample_blockchains::consensus_constants(network).build(); let (gen_block, _) = create_genesis_block(&consensus_constants, &key_manager).await; let consensus_manager = ConsensusManagerBuilder::new(network) .add_consensus_constants(consensus_constants)