diff --git a/core/src/blend/mod.rs b/core/src/blend/mod.rs index 96b4854f0..3fdc9d728 100644 --- a/core/src/blend/mod.rs +++ b/core/src/blend/mod.rs @@ -1,12 +1,17 @@ use std::num::NonZeroU64; use lb_blend_proofs::quota::Quota; -use lb_utils::math::NonNegativeF64; +use lb_utils::math::PositiveF64; +/// `Q_C`: the messaging allowance of a single core node for one epoch. +/// +/// The spec requires the parameters to satisfy `C * ß_c > 0`, which the +/// argument types carry: `message_frequency_per_round` is positive and +/// `num_blend_layers` is non-zero, so `Q_C >= 1` for every epoch. #[must_use] pub fn core_quota( rounds_per_epoch: NonZeroU64, - message_frequency_per_round: NonNegativeF64, + message_frequency_per_round: PositiveF64, num_blend_layers: NonZeroU64, membership_size: usize, ) -> Quota { @@ -18,8 +23,14 @@ pub fn core_quota( // `Q_c`: Messaging allowance that can be used by a core node during a single // epoch. We assume `R_c` to be `0` for now, hence `Q_c = ceil(C * (ß_c // + 0 * ß_c)) / N = ceil(C * ß_c) / N`. - (((expected_number_of_epoch_messages * num_blend_layers.get() as f64) / membership_size as f64) - .ceil() as u64) + let quota_integer = NonZeroU64::try_from( + ((expected_number_of_epoch_messages * num_blend_layers.get() as f64) + / membership_size as f64) + .ceil() as u64, + ) + .expect("Core Quota cannot be zero, if `message_frequency_per_round` is greater than zero and `num_blend_layers` is non-zero."); + quota_integer + .get() .try_into() .expect("Core Quota must fit within the width the `PoQ` circuit allows.") } diff --git a/ledger/src/config.rs b/ledger/src/config.rs index ff7c203cc..25a955795 100644 --- a/ledger/src/config.rs +++ b/ledger/src/config.rs @@ -142,7 +142,7 @@ mod tests { use lb_core::sdp::{MinStake, ServiceParameters, ServiceType}; use lb_cryptarchia_engine::EpochConfig; pub use lb_groth16::ModulusShift; - use lb_utils::math::{NonNegativeF64, NonNegativeRatio}; + use lb_utils::math::{NonNegativeRatio, PositiveF64}; use crate::{ config::{BlendPoWConfig, PoWConfig}, @@ -181,7 +181,7 @@ mod tests { service_rewards_params: ServiceRewardsParameters { blend: RewardsParameters { rounds_per_epoch: epoch_length.try_into().unwrap(), - message_frequency_per_round: NonNegativeF64::try_from(1.0).unwrap(), + message_frequency_per_round: PositiveF64::try_from(1.0).unwrap(), num_blend_layers: NonZeroU64::new(3).unwrap(), minimum_network_size: NonZeroU64::new(1).unwrap(), data_replication_factor: 0, @@ -243,7 +243,7 @@ mod tests { service_rewards_params: ServiceRewardsParameters { blend: RewardsParameters { rounds_per_epoch: epoch_length.try_into().unwrap(), - message_frequency_per_round: NonNegativeF64::try_from(1.0).unwrap(), + message_frequency_per_round: PositiveF64::try_from(1.0).unwrap(), num_blend_layers: NonZeroU64::new(3).unwrap(), minimum_network_size: NonZeroU64::new(1).unwrap(), data_replication_factor: 0, @@ -314,7 +314,7 @@ mod tests { service_rewards_params: ServiceRewardsParameters { blend: RewardsParameters { rounds_per_epoch: epoch_length.try_into().unwrap(), - message_frequency_per_round: NonNegativeF64::try_from(1.0).unwrap(), + message_frequency_per_round: PositiveF64::try_from(1.0).unwrap(), num_blend_layers: NonZeroU64::new(3).unwrap(), minimum_network_size: NonZeroU64::new(1).unwrap(), data_replication_factor: 0, diff --git a/ledger/src/cryptarchia/mod.rs b/ledger/src/cryptarchia/mod.rs index 77fe196aa..1395dfd00 100644 --- a/ledger/src/cryptarchia/mod.rs +++ b/ledger/src/cryptarchia/mod.rs @@ -864,7 +864,7 @@ pub mod tests { use lb_cryptarchia_engine::EpochConfig; use lb_groth16::{AdditiveGroup as _, ModulusShift}; use lb_key_management_system_keys::keys::{Ed25519Key, Ed25519PublicKey, ZkKey, ZkSignature}; - use lb_utils::math::{NonNegativeF64, NonNegativeRatio}; + use lb_utils::math::{NonNegativeRatio, PositiveF64}; use num_bigint::BigUint; use rand::{RngCore as _, thread_rng}; @@ -1074,7 +1074,7 @@ pub mod tests { service_rewards_params: ServiceRewardsParameters { blend: rewards::blend::RewardsParameters { rounds_per_epoch: epoch_length.try_into().unwrap(), - message_frequency_per_round: NonNegativeF64::try_from(1.0).unwrap(), + message_frequency_per_round: PositiveF64::try_from(1.0).unwrap(), num_blend_layers: NonZeroU64::new(3).unwrap(), minimum_network_size: NonZeroU64::new(1).unwrap(), data_replication_factor: 0, diff --git a/ledger/src/mantle/sdp/mod.rs b/ledger/src/mantle/sdp/mod.rs index 92db2ee4a..546865352 100644 --- a/ledger/src/mantle/sdp/mod.rs +++ b/ledger/src/mantle/sdp/mod.rs @@ -642,7 +642,7 @@ mod tests { }; use lb_groth16::{AdditiveGroup as _, Fr}; use lb_key_management_system_keys::keys::{Ed25519Key, ZkKey}; - use lb_utils::math::NonNegativeF64; + use lb_utils::math::PositiveF64; use num_bigint::BigUint; use super::*; @@ -658,7 +658,7 @@ mod tests { service_rewards_params: ServiceRewardsParameters { blend: blend::RewardsParameters { rounds_per_epoch: NonZeroU64::new(10).unwrap(), - message_frequency_per_round: NonNegativeF64::try_from(1.0).unwrap(), + message_frequency_per_round: PositiveF64::try_from(1.0).unwrap(), num_blend_layers: NonZeroU64::new(3).unwrap(), minimum_network_size: NonZeroU64::new(1).unwrap(), data_replication_factor: 0, diff --git a/ledger/src/mantle/sdp/rewards/blend/mod.rs b/ledger/src/mantle/sdp/rewards/blend/mod.rs index 94689e3fc..4a4ca3f45 100644 --- a/ledger/src/mantle/sdp/rewards/blend/mod.rs +++ b/ledger/src/mantle/sdp/rewards/blend/mod.rs @@ -15,7 +15,7 @@ use lb_core::{ mantle::{Utxo, Value}, sdp::{ActivityMetadata, ProviderId, ServiceParameters}, }; -use lb_utils::math::NonNegativeF64; +use lb_utils::math::PositiveF64; use tracing::debug; use crate::{ @@ -233,7 +233,7 @@ where #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct RewardsParameters { pub rounds_per_epoch: NonZeroU64, - pub message_frequency_per_round: NonNegativeF64, + pub message_frequency_per_round: PositiveF64, pub num_blend_layers: NonZeroU64, pub data_replication_factor: u64, pub minimum_network_size: NonZeroU64, @@ -327,7 +327,7 @@ mod tests { ) -> RewardsParameters { RewardsParameters { rounds_per_epoch: rounds_per_epoch.try_into().unwrap(), - message_frequency_per_round: NonNegativeF64::try_from(1.0).unwrap(), + message_frequency_per_round: PositiveF64::try_from(1.0).unwrap(), num_blend_layers: NonZeroU64::new(3).unwrap(), minimum_network_size: minimum_network_size.try_into().unwrap(), data_replication_factor: 0, diff --git a/nodes/node/binary/src/config/blend/deployment.rs b/nodes/node/binary/src/config/blend/deployment.rs index d57dfb608..03574e9cd 100644 --- a/nodes/node/binary/src/config/blend/deployment.rs +++ b/nodes/node/binary/src/config/blend/deployment.rs @@ -2,7 +2,7 @@ use core::{num::NonZeroU64, time::Duration}; use lb_ledger::mantle::sdp::rewards::blend::RewardsParameters; use lb_libp2p::protocol_name::StreamProtocol; -use lb_utils::math::NonNegativeF64; +use lb_utils::math::{NonNegativeF64, PositiveF64}; use nutype::nutype; use serde::{Deserialize, Serialize}; @@ -127,7 +127,7 @@ pub struct SchedulerSettings { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct CoverTrafficSettings { /// `F_c`: frequency at which cover messages are generated per round. - pub message_frequency_per_round: NonNegativeF64, + pub message_frequency_per_round: PositiveF64, } #[derive(Serialize, Deserialize, Clone, Debug)] diff --git a/services/blend/src/core/settings.rs b/services/blend/src/core/settings.rs index f54d6ac04..58945d49f 100644 --- a/services/blend/src/core/settings.rs +++ b/services/blend/src/core/settings.rs @@ -4,7 +4,7 @@ use lb_core::blend::core_quota; use lb_key_management_system_service::{backend::preload::KeyId, keys::UnsecuredEd25519Key}; use lb_poq::Quota; use lb_services_utils::overwatch::{RecoveryData, StorageRecoverySettings}; -use lb_utils::math::NonNegativeF64; +use lb_utils::math::PositiveF64; use serde::{Deserialize, Serialize}; use crate::settings::TimingSettings; @@ -103,7 +103,7 @@ pub struct SchedulerSettings { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct CoverTrafficSettings { /// `F_c`: frequency at which cover messages are generated per round. - pub message_frequency_per_round: NonNegativeF64, + pub message_frequency_per_round: PositiveF64, } #[cfg(test)] diff --git a/services/blend/src/core/tests/mod.rs b/services/blend/src/core/tests/mod.rs index 8a411b8d0..70e957124 100644 --- a/services/blend/src/core/tests/mod.rs +++ b/services/blend/src/core/tests/mod.rs @@ -1,4 +1,4 @@ -use core::time::Duration; +use core::{num::NonZeroU64, time::Duration}; use std::collections::VecDeque; use futures::{StreamExt as _, stream::repeat}; @@ -1854,8 +1854,14 @@ async fn a_transaction_awaiting_a_pow_solution_does_not_stall_the_event_loop() { 0, ); // No cover traffic, so every message the service sends is one this test put - // in and the count below means what it says. - settings.scheduler.cover.message_frequency_per_round = 0.0.try_into().unwrap(); + // in and the count below means what it says. The frequency itself must stay + // positive (`C * ß_c > 0`), so the silence comes from the quota instead: + // `Q_c = ceil(10 rounds * 0.05 * 2 layers / 2 nodes) = ceil(0.5) = 1`, and + // the scheduler floors its cover count at `Q_c / num_blend_layers = 0`. + // Anything in `(0, 0.1]` gives the same quota, so the halfway point keeps + // float drift well clear of either rounding boundary. + settings.num_blend_layers = NonZeroU64::try_from(2).unwrap(); + settings.scheduler.cover.message_frequency_per_round = 0.05.try_into().unwrap(); let (inbound_relay, inbound_message_sender) = new_stream(); let (mut blend_message_stream, _blend_message_sender) = new_stream(); diff --git a/services/chain/chain-leader/src/leadership.rs b/services/chain/chain-leader/src/leadership.rs index 783b1315c..5feb0800d 100644 --- a/services/chain/chain-leader/src/leadership.rs +++ b/services/chain/chain-leader/src/leadership.rs @@ -516,7 +516,7 @@ mod pol_tests { Config as SdpConfig, ServiceRewardsParameters, rewards::blend::RewardsParameters, }, }; - use lb_utils::math::{NonNegativeF64, NonNegativeRatio}; + use lb_utils::math::{NonNegativeRatio, PositiveF64}; use lb_wallet_service::{WalletMsg, WalletServiceSettings}; use overwatch::services::{ ServiceData, @@ -749,7 +749,7 @@ mod pol_tests { service_rewards_params: ServiceRewardsParameters { blend: RewardsParameters { rounds_per_epoch: NonZero::new(10u64).unwrap(), - message_frequency_per_round: NonNegativeF64::try_from(1.0).unwrap(), + message_frequency_per_round: PositiveF64::try_from(1.0).unwrap(), num_blend_layers: NonZero::new(3u64).unwrap(), minimum_network_size: NonZero::new(1u64).unwrap(), data_replication_factor: 0, diff --git a/services/chain/chain-network/src/bootstrap/ibd.rs b/services/chain/chain-network/src/bootstrap/ibd.rs index 095c2bfc3..7c238e918 100644 --- a/services/chain/chain-network/src/bootstrap/ibd.rs +++ b/services/chain/chain-network/src/bootstrap/ibd.rs @@ -367,7 +367,7 @@ mod tests { mantle::sdp::{ServiceRewardsParameters, rewards}, }; use lb_network_service::{NetworkService, backends::NetworkBackend, message::ChainSyncEvent}; - use lb_utils::math::{NonNegativeF64, NonNegativeRatio}; + use lb_utils::math::{NonNegativeRatio, PositiveF64}; use overwatch::{ overwatch::OverwatchHandle, services::{ServiceData, relay::OutboundRelay}, @@ -973,7 +973,7 @@ mod tests { service_rewards_params: ServiceRewardsParameters { blend: rewards::blend::RewardsParameters { rounds_per_epoch: epoch_length.try_into().unwrap(), - message_frequency_per_round: NonNegativeF64::try_from(1.0).unwrap(), + message_frequency_per_round: PositiveF64::try_from(1.0).unwrap(), num_blend_layers: NonZeroU64::new(3).unwrap(), minimum_network_size: NonZeroU64::new(1).unwrap(), data_replication_factor: 0, diff --git a/services/chain/chain-service/src/states.rs b/services/chain/chain-service/src/states.rs index 712c8ce7b..b1da6a8bc 100644 --- a/services/chain/chain-service/src/states.rs +++ b/services/chain/chain-service/src/states.rs @@ -136,7 +136,7 @@ mod tests { config::{BlendPoWConfig, ModulusShift, PoWConfig}, mantle::sdp::{ServiceRewardsParameters, rewards}, }; - use lb_utils::math::{NonNegativeF64, NonNegativeRatio}; + use lb_utils::math::{NonNegativeRatio, PositiveF64}; use super::*; @@ -177,7 +177,7 @@ mod tests { service_rewards_params: ServiceRewardsParameters { blend: rewards::blend::RewardsParameters { rounds_per_epoch: epoch_length.try_into().unwrap(), - message_frequency_per_round: NonNegativeF64::try_from(1.0).unwrap(), + message_frequency_per_round: PositiveF64::try_from(1.0).unwrap(), num_blend_layers: NonZeroU64::new(3).unwrap(), minimum_network_size: NonZeroU64::new(1).unwrap(), data_replication_factor: 0, @@ -360,7 +360,7 @@ mod tests { service_rewards_params: ServiceRewardsParameters { blend: rewards::blend::RewardsParameters { rounds_per_epoch: epoch_length.try_into().unwrap(), - message_frequency_per_round: NonNegativeF64::try_from(1.0).unwrap(), + message_frequency_per_round: PositiveF64::try_from(1.0).unwrap(), num_blend_layers: NonZeroU64::new(3).unwrap(), minimum_network_size: NonZeroU64::new(1).unwrap(), data_replication_factor: 0, diff --git a/tools/config/src/deployment.rs b/tools/config/src/deployment.rs index c6da3ec7b..005dbef41 100644 --- a/tools/config/src/deployment.rs +++ b/tools/config/src/deployment.rs @@ -24,7 +24,7 @@ use lb_node::config::{ network::deployment::Settings as NetworkDeploymentSettings, time::deployment::Settings as TimeDeploymentSettings, }; -use lb_utils::math::{NonNegativeF64, NonNegativeRatio}; +use lb_utils::math::{NonNegativeRatio, PositiveF64}; use crate::{ release::ProtocolIdentity, @@ -97,10 +97,10 @@ pub fn e2e_deployment_settings_with_genesis_block( .expect("Normalization constant cannot be negative."), scheduler: SchedulerSettings { cover: CoverTrafficSettings { - message_frequency_per_round: NonNegativeF64::try_from( + message_frequency_per_round: PositiveF64::try_from( COVER_MESSAGE_FREQUENCY_PER_ROUND, ) - .expect("Message frequency per round cannot be negative."), + .expect("Message frequency per round must be positive."), }, delayer: MessageDelayerSettings { maximum_release_delay_in_rounds: NonZeroU64::try_from( diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 499e4d750..d4a04506c 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -898,7 +898,7 @@ mod tests { mantle::sdp::{ServiceRewardsParameters, rewards}, }; use lb_pol::LotteryConstants; - use lb_utils::math::{NonNegativeF64, NonNegativeRatio}; + use lb_utils::math::{NonNegativeRatio, PositiveF64}; use lb_utxotree::UtxoTree; use num_bigint::BigUint; use rpds::HashTrieSetSync; @@ -1882,7 +1882,7 @@ mod tests { service_rewards_params: ServiceRewardsParameters { blend: rewards::blend::RewardsParameters { rounds_per_epoch: epoch_length.try_into().unwrap(), - message_frequency_per_round: NonNegativeF64::try_from(1.0).unwrap(), + message_frequency_per_round: PositiveF64::try_from(1.0).unwrap(), num_blend_layers: NonZeroU64::new(3).unwrap(), minimum_network_size: NonZeroU64::new(1).unwrap(), data_replication_factor: 0,