diff --git a/deny.toml b/deny.toml index 874824fe3..b10772ef6 100644 --- a/deny.toml +++ b/deny.toml @@ -52,7 +52,7 @@ exceptions = [ { allow = ["AGPL-3.0"], name = "serai-coins-pallet" }, { allow = ["AGPL-3.0"], name = "serai-dex-pallet" }, - + { allow = ["AGPL-3.0"], name = "serai-genesis-liquidity-pallet" }, { allow = ["AGPL-3.0"], name = "serai-emissions-pallet" }, diff --git a/substrate/abi/src/lib.rs b/substrate/abi/src/lib.rs index ae25c89ee..c45a89356 100644 --- a/substrate/abi/src/lib.rs +++ b/substrate/abi/src/lib.rs @@ -16,12 +16,14 @@ pub mod liquidity_tokens; pub mod dex; pub mod validator_sets; -pub mod in_instructions; -pub mod signals; pub mod genesis_liquidity; pub mod emissions; +pub mod in_instructions; + +pub mod signals; + pub mod babe; pub mod grandpa; diff --git a/substrate/client/tests/validator_sets.rs b/substrate/client/tests/validator_sets.rs index 3c8ce676d..c2c6c509d 100644 --- a/substrate/client/tests/validator_sets.rs +++ b/substrate/client/tests/validator_sets.rs @@ -336,7 +336,7 @@ async fn verify_session_and_active_validators( let mut block = serai.latest_finalized_block_hash().await.unwrap(); if session_for_block(serai, block, network).await < session { // Sleep a block - tokio::time::sleep(core::time::Duration::from_secs(6)).await; + tokio::time::sleep(core::time::Duration::from_secs(TARGET_BLOCK_TIME)).await; continue; } while session_for_block(serai, block, network).await > session { @@ -366,7 +366,7 @@ async fn verify_session_and_active_validators( tokio::time::timeout(core::time::Duration::from_secs(TARGET_BLOCK_TIME * 10), async move { let mut finalized_block = serai.latest_finalized_block().await.unwrap().header.number; while finalized_block <= current_finalized_block + 2 { - tokio::time::sleep(core::time::Duration::from_secs(6)).await; + tokio::time::sleep(core::time::Duration::from_secs(TARGET_BLOCK_TIME)).await; finalized_block = serai.latest_finalized_block().await.unwrap().header.number; } }) diff --git a/substrate/coins/pallet/src/lib.rs b/substrate/coins/pallet/src/lib.rs index 1dd686340..f6b055f70 100644 --- a/substrate/coins/pallet/src/lib.rs +++ b/substrate/coins/pallet/src/lib.rs @@ -159,7 +159,9 @@ pub mod pallet { /// /// Errors if any amount overflows. pub fn mint(to: Public, balance: Balance) -> Result<(), Error> { - if balance.coin != Coin::Serai && !T::AllowMint::is_allowed(&balance) { + // If the coin isn't Serai, which we're always allowed to mint, and the mint isn't explicitly + // allowed, error + if (balance.coin != Coin::Serai) && (!T::AllowMint::is_allowed(&balance)) { Err(Error::::MintNotAllowed)?; } diff --git a/substrate/emissions/pallet/src/lib.rs b/substrate/emissions/pallet/src/lib.rs index 9569e85ca..66310d5cd 100644 --- a/substrate/emissions/pallet/src/lib.rs +++ b/substrate/emissions/pallet/src/lib.rs @@ -60,6 +60,7 @@ pub mod pallet { #[pallet::pallet] pub struct Pallet(PhantomData); + // TODO: Remove this. This should be the sole domain of validator-sets #[pallet::storage] #[pallet::getter(fn participants)] pub(crate) type Participants = StorageMap< @@ -70,21 +71,23 @@ pub mod pallet { OptionQuery, >; + // TODO: Remove this too #[pallet::storage] #[pallet::getter(fn session)] pub type CurrentSession = StorageMap<_, Identity, NetworkId, u32, ValueQuery>; + // TODO: Find a better place for this #[pallet::storage] #[pallet::getter(fn economic_security_reached)] pub(crate) type EconomicSecurityReached = StorageMap<_, Identity, NetworkId, bool, ValueQuery>; + // TODO: Find a better place for this #[pallet::storage] - #[pallet::getter(fn last_swap_volume)] - pub(crate) type LastSwapVolume = StorageMap<_, Identity, Coin, u64, OptionQuery>; + pub(crate) type GenesisCompleteBlock = StorageValue<_, u64, OptionQuery>; #[pallet::storage] - pub(crate) type GenesisCompleteBlock = StorageValue<_, u64, OptionQuery>; + pub(crate) type LastSwapVolume = StorageMap<_, Identity, Coin, u64, OptionQuery>; #[pallet::genesis_build] impl BuildGenesisConfig for GenesisConfig { @@ -138,20 +141,20 @@ pub mod pallet { CurrentSession::::set(NetworkId::Serai, session.0); } - // update participants per session before the genesis and after the genesis - // we update them after reward distribution. - if !genesis_ended && session_changed { + // update participants per session before the genesis + // after the genesis, we update them after reward distribution. + if (!genesis_ended) && session_changed { Self::update_participants(); } - // emissions start only after genesis period and happens once per session. - // so we don't do anything before that time. + // We only want to distribute emissions if the genesis period is over AND the session has + // ended if !(genesis_ended && session_changed) { return Weight::zero(); // TODO } - // figure out the amount of blocks in the last session. Session is at least 1 - // if we come here. + // figure out the amount of blocks in the last session + // Since the session has changed, we're now at least at session 1 let block_count = ValidatorSets::::session_begin_block(NetworkId::Serai, session) - ValidatorSets::::session_begin_block(NetworkId::Serai, Session(session.0 - 1)); @@ -177,9 +180,9 @@ pub mod pallet { total_distance = total_distance.saturating_add(distance); } - // add serai network portion(20%) + // add serai network portion (20%) let new_total_distance = - total_distance.saturating_mul(10) / (10 - SERAI_VALIDATORS_DESIRED_PERCENTAGE); + total_distance.saturating_mul(100) / (100 - SERAI_VALIDATORS_DESIRED_PERCENTAGE); distances.insert(NetworkId::Serai, new_total_distance - total_distance); total_distance = new_total_distance; @@ -221,7 +224,7 @@ pub mod pallet { for c in COINS { // this should return 0 for SRI and so it shouldn't affect the total volume. let current_volume = Dex::::swap_volume(c).unwrap_or(0); - let last_volume = Self::last_swap_volume(c).unwrap_or(0); + let last_volume = LastSwapVolume::::get(c).unwrap_or(0); let vol_this_epoch = current_volume.saturating_sub(last_volume); // update the current volume @@ -377,7 +380,7 @@ pub mod pallet { .unwrap(); Coins::::mint(p, Balance { coin: Coin::Serai, amount: Amount(p_reward) }).unwrap(); - if ValidatorSets::::deposit_stake(n, p, Amount(p_reward)).is_err() { + if ValidatorSets::::distribute_block_rewards(n, p, Amount(p_reward)).is_err() { // TODO: log the failure continue; } @@ -432,14 +435,11 @@ pub mod pallet { Coins::::mint(to, Balance { coin: Coin::Serai, amount: sri_amount })?; // Stake the SRI for the network. - let allocation_per_key_share = - ValidatorSets::::allocation_per_key_share(network).unwrap().0; - let allocation = ValidatorSets::::allocation((network, to)).unwrap_or(Amount(0)).0; - if allocation.saturating_add(sri_amount.0) < allocation_per_key_share { - Err(Error::::InsufficientAllocation)?; - } - - ValidatorSets::::deposit_stake(network, to, sri_amount)?; + ValidatorSets::::allocate( + frame_system::RawOrigin::Signed(to).into(), + network, + sri_amount, + )?; Ok(()) } diff --git a/substrate/emissions/primitives/src/lib.rs b/substrate/emissions/primitives/src/lib.rs index efc9aa5e1..38aa8ca2e 100644 --- a/substrate/emissions/primitives/src/lib.rs +++ b/substrate/emissions/primitives/src/lib.rs @@ -2,22 +2,19 @@ #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(not(feature = "std"), no_std)] -use serai_primitives::{SeraiAddress, system_address}; +use serai_primitives::{DAYS, YEARS, SeraiAddress, system_address}; // Protocol owned liquidity account. -pub const POL_ACCOUNT: SeraiAddress = system_address(b"PoL-account"); - -/// Amount of blocks in 30 days for 6s per block. -const BLOCKS_PER_MONTH: u64 = 10 * 60 * 24 * 30; +pub const POL_ACCOUNT: SeraiAddress = system_address(b"Serai-protocol_owned_liquidity"); /// INITIAL_REWARD = 100,000 SRI / BLOCKS_PER_DAY for 60 days -pub const INITIAL_REWARD_PER_BLOCK: u64 = 100_000 * 10u64.pow(8) / (BLOCKS_PER_MONTH / 30); +pub const INITIAL_REWARD_PER_BLOCK: u64 = (100_000 * 10u64.pow(8)) / DAYS; /// REWARD = 20M SRI / BLOCKS_PER_YEAR -pub const REWARD_PER_BLOCK: u64 = 20_000_000 * 10u64.pow(8) / (BLOCKS_PER_MONTH * 12); +pub const REWARD_PER_BLOCK: u64 = (20_000_000 * 10u64.pow(8)) / YEARS; -/// 20% of all stake desired to be for Serai network(2/10) -pub const SERAI_VALIDATORS_DESIRED_PERCENTAGE: u64 = 2; +/// 20% of all stake desired to be for Serai network +pub const SERAI_VALIDATORS_DESIRED_PERCENTAGE: u64 = 20; /// Desired unused capacity ratio for a network assuming capacity is 10,000. pub const DESIRED_DISTRIBUTION: u64 = 1_000; @@ -26,4 +23,4 @@ pub const DESIRED_DISTRIBUTION: u64 = 1_000; pub const ACCURACY_MULTIPLIER: u64 = 10_000; /// The block to target for economic security -pub const SECURE_BY: u64 = BLOCKS_PER_MONTH * 12; +pub const SECURE_BY: u64 = YEARS; diff --git a/substrate/primitives/src/constants.rs b/substrate/primitives/src/constants.rs index 7874a2087..b3db73178 100644 --- a/substrate/primitives/src/constants.rs +++ b/substrate/primitives/src/constants.rs @@ -7,13 +7,16 @@ pub const TARGET_BLOCK_TIME: u64 = 6; /// Measured in blocks. pub const MINUTES: BlockNumber = 60 / TARGET_BLOCK_TIME; -pub const HOURS: BlockNumber = MINUTES * 60; -pub const DAYS: BlockNumber = HOURS * 24; -pub const WEEKS: BlockNumber = DAYS * 7; -pub const MONTHS: BlockNumber = WEEKS * 4; +pub const HOURS: BlockNumber = 60 * MINUTES; +pub const DAYS: BlockNumber = 24 * HOURS; +pub const WEEKS: BlockNumber = 7 * DAYS; +// Defines a month as 30 days, which is slightly inaccurate +pub const MONTHS: BlockNumber = 30 * DAYS; +// Defines a year as 12 inaccurate months, which is 360 days literally (~1.5% off) +pub const YEARS: BlockNumber = 12 * MONTHS; /// 6 months of blocks -pub const GENESIS_SRI_TRICKLE_FEED: u64 = MONTHS * 6; +pub const GENESIS_SRI_TRICKLE_FEED: u64 = 6 * MONTHS; // 100 Million SRI pub const GENESIS_SRI: u64 = 100_000_000 * 10_u64.pow(8); @@ -31,5 +34,5 @@ pub const MEDIAN_PRICE_WINDOW_LENGTH: u16 = (2 * ARBITRAGE_TIME) + 1; /// Amount of blocks per epoch in the fast-epoch feature that is used in tests. pub const FAST_EPOCH_DURATION: u64 = 2 * MINUTES; -/// Amount of blocks for the initial period era for the emissions for fast epoc feature. -pub const FAST_EPOCH_INITIAL_PERIOD: u64 = FAST_EPOCH_DURATION * 2; +/// Amount of blocks for the initial period era of the emissions under the fast-epoch feature. +pub const FAST_EPOCH_INITIAL_PERIOD: u64 = 2 * FAST_EPOCH_DURATION; diff --git a/substrate/validator-sets/pallet/src/lib.rs b/substrate/validator-sets/pallet/src/lib.rs index c9b94019f..a404ae732 100644 --- a/substrate/validator-sets/pallet/src/lib.rs +++ b/substrate/validator-sets/pallet/src/lib.rs @@ -263,12 +263,20 @@ pub mod pallet { _t: PhantomData, prefix: Vec, last: Vec, + allocation_per_key_share: Amount, } impl SortedAllocationsIter { fn new(network: NetworkId) -> Self { let mut prefix = SortedAllocations::::final_prefix().to_vec(); prefix.extend(&network.encode()); - Self { _t: PhantomData, prefix: prefix.clone(), last: prefix } + Self { + _t: PhantomData, + prefix: prefix.clone(), + last: prefix, + allocation_per_key_share: Pallet::::allocation_per_key_share(network).expect( + "SortedAllocationsIter iterating over a network without a set allocation per key share", + ), + } } } impl Iterator for SortedAllocationsIter { @@ -276,10 +284,17 @@ pub mod pallet { fn next(&mut self) -> Option { let next = sp_io::storage::next_key(&self.last)?; if !next.starts_with(&self.prefix) { - return None; + None?; } let key = Pallet::::recover_key_from_sorted_allocation_key(&next); let amount = Pallet::::recover_amount_from_sorted_allocation_key(&next); + + // We may have validators present, with less than the minimum allocation, due to block + // rewards + if amount.0 < self.allocation_per_key_share.0 { + None?; + } + self.last = next; Some((key, amount)) } @@ -310,7 +325,7 @@ pub mod pallet { #[pallet::storage] pub type SeraiDisabledIndices = StorageMap<_, Identity, u32, Public, OptionQuery>; - /// Mapping from session to block number + /// Mapping from session to its starting block number. #[pallet::storage] #[pallet::getter(fn session_begin_block)] pub type SessionBeginBlock = @@ -507,7 +522,8 @@ pub mod pallet { let old_allocation = Self::allocation((network, account)).unwrap_or(Amount(0)).0; let new_allocation = old_allocation + amount.0; let allocation_per_key_share = Self::allocation_per_key_share(network).unwrap().0; - if new_allocation < allocation_per_key_share && !block_reward { + // If this is a block reward, we always allow it to be allocated + if (new_allocation < allocation_per_key_share) && (!block_reward) { Err(Error::::InsufficientAllocation)?; } @@ -832,7 +848,7 @@ pub mod pallet { total_required } - pub fn deposit_stake( + pub fn distribute_block_rewards( network: NetworkId, account: T::AccountId, amount: Amount,