diff --git a/sdk/src/types/block/mana/allotment.rs b/sdk/src/types/block/mana/allotment.rs index dc9295c11d..20a771148b 100644 --- a/sdk/src/types/block/mana/allotment.rs +++ b/sdk/src/types/block/mana/allotment.rs @@ -1,7 +1,12 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use packable::Packable; +use alloc::{boxed::Box, collections::BTreeSet, vec::Vec}; +use core::ops::RangeInclusive; + +use derive_more::Deref; +use iterator_sorted::is_unique_sorted; +use packable::{bounded::BoundedU16, prefix::BoxedSlicePrefix, Packable}; use crate::types::block::{ output::AccountId, @@ -62,6 +67,127 @@ fn verify_mana(mana: &u64, params: &ProtocolParameters) -> R Ok(()) } +pub(crate) type ManaAllotmentCount = + BoundedU16<{ *ManaAllotments::COUNT_RANGE.start() }, { *ManaAllotments::COUNT_RANGE.end() }>; + +/// A list of [`ManaAllotment`]s with unique [`AccountId`]s. +#[derive(Clone, Debug, Eq, PartialEq, Deref, Packable)] +#[packable(unpack_visitor = ProtocolParameters)] +#[packable(unpack_error = Error, with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidManaAllotmentCount(p.into())))] +pub struct ManaAllotments( + #[packable(verify_with = verify_mana_allotments)] BoxedSlicePrefix, +); + +impl ManaAllotments { + /// The minimum number of mana allotments of a transaction. + pub const COUNT_MIN: u16 = 0; + /// The maximum number of mana allotments of a transaction. + pub const COUNT_MAX: u16 = 128; + /// The range of valid numbers of mana allotments of a transaction. + pub const COUNT_RANGE: RangeInclusive = Self::COUNT_MIN..=Self::COUNT_MAX; // [1..128] + + /// Creates a new [`ManaAllotments`] from a vec. + pub fn from_vec(allotments: Vec) -> Result { + verify_mana_allotments_unique_sorted(&allotments)?; + + Ok(Self( + allotments + .into_boxed_slice() + .try_into() + .map_err(Error::InvalidManaAllotmentCount)?, + )) + } + + /// Creates a new [`ManaAllotments`] from an ordered set. + pub fn from_set(allotments: BTreeSet) -> Result { + Ok(Self( + allotments + .into_iter() + .collect::>() + .try_into() + .map_err(Error::InvalidManaAllotmentCount)?, + )) + } + + /// Gets a reference to an [`ManaAllotment`], if one exists, using an [`AccountId`]. + #[inline(always)] + pub fn get(&self, account_id: &AccountId) -> Option<&ManaAllotment> { + self.0.iter().find(|a| a.account_id() == account_id) + } +} + +fn verify_mana_allotments( + allotments: &[ManaAllotment], + protocol_params: &ProtocolParameters, +) -> Result<(), Error> { + if VERIFY { + verify_mana_allotments_unique_sorted(allotments)?; + verify_mana_allotments_sum(allotments, protocol_params)?; + } + + Ok(()) +} + +fn verify_mana_allotments_unique_sorted<'a>( + allotments: impl IntoIterator, +) -> Result<(), Error> { + if !is_unique_sorted(allotments.into_iter()) { + return Err(Error::ManaAllotmentsNotUniqueSorted); + } + Ok(()) +} + +pub(crate) fn verify_mana_allotments_sum<'a>( + allotments: impl IntoIterator, + protocol_params: &ProtocolParameters, +) -> Result<(), Error> { + let mut mana_sum: u64 = 0; + let max_mana = protocol_params.mana_parameters().max_mana(); + + for ManaAllotment { mana, .. } in allotments { + mana_sum = mana_sum.checked_add(*mana).ok_or(Error::InvalidManaAllotmentSum { + sum: mana_sum as u128 + *mana as u128, + max: max_mana, + })?; + + if mana_sum > max_mana { + return Err(Error::InvalidManaAllotmentSum { + sum: mana_sum as u128, + max: max_mana, + }); + } + } + + Ok(()) +} + +impl TryFrom> for ManaAllotments { + type Error = Error; + + #[inline(always)] + fn try_from(allotments: Vec) -> Result { + Self::from_vec(allotments) + } +} + +impl TryFrom> for ManaAllotments { + type Error = Error; + + #[inline(always)] + fn try_from(allotments: BTreeSet) -> Result { + Self::from_set(allotments) + } +} + +impl IntoIterator for ManaAllotments { + type Item = ManaAllotment; + type IntoIter = alloc::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + Vec::from(Into::>::into(self.0)).into_iter() + } +} + #[cfg(feature = "serde")] pub(super) mod dto { use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/mana/mod.rs b/sdk/src/types/block/mana/mod.rs index 72328c74db..f9eb15e924 100644 --- a/sdk/src/types/block/mana/mod.rs +++ b/sdk/src/types/block/mana/mod.rs @@ -5,135 +5,11 @@ mod allotment; mod parameters; mod rewards; -use alloc::{boxed::Box, collections::BTreeSet, vec::Vec}; -use core::ops::RangeInclusive; - -use derive_more::Deref; -use iterator_sorted::is_unique_sorted; -use packable::{bounded::BoundedU16, prefix::BoxedSlicePrefix, Packable}; - #[cfg(feature = "serde")] pub use self::allotment::dto::ManaAllotmentDto; -pub use self::{allotment::ManaAllotment, parameters::ManaParameters, rewards::RewardsParameters}; -use super::{output::AccountId, protocol::ProtocolParameters, Error}; - -pub(crate) type ManaAllotmentCount = - BoundedU16<{ *ManaAllotments::COUNT_RANGE.start() }, { *ManaAllotments::COUNT_RANGE.end() }>; - -/// A list of [`ManaAllotment`]s with unique [`AccountId`]s. -#[derive(Clone, Debug, Eq, PartialEq, Deref, Packable)] -#[packable(unpack_visitor = ProtocolParameters)] -#[packable(unpack_error = Error, with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidManaAllotmentCount(p.into())))] -pub struct ManaAllotments( - #[packable(verify_with = verify_mana_allotments)] BoxedSlicePrefix, -); - -impl ManaAllotments { - /// The minimum number of mana allotments of a transaction. - pub const COUNT_MIN: u16 = 0; - /// The maximum number of mana allotments of a transaction. - pub const COUNT_MAX: u16 = 128; - /// The range of valid numbers of mana allotments of a transaction. - pub const COUNT_RANGE: RangeInclusive = Self::COUNT_MIN..=Self::COUNT_MAX; // [1..128] - - /// Creates a new [`ManaAllotments`] from a vec. - pub fn from_vec(allotments: Vec) -> Result { - verify_mana_allotments_unique_sorted(&allotments)?; - - Ok(Self( - allotments - .into_boxed_slice() - .try_into() - .map_err(Error::InvalidManaAllotmentCount)?, - )) - } - - /// Creates a new [`ManaAllotments`] from an ordered set. - pub fn from_set(allotments: BTreeSet) -> Result { - Ok(Self( - allotments - .into_iter() - .collect::>() - .try_into() - .map_err(Error::InvalidManaAllotmentCount)?, - )) - } - - /// Gets a reference to an [`ManaAllotment`], if one exists, using an [`AccountId`]. - #[inline(always)] - pub fn get(&self, account_id: &AccountId) -> Option<&ManaAllotment> { - self.0.iter().find(|a| a.account_id() == account_id) - } -} - -fn verify_mana_allotments( - allotments: &[ManaAllotment], - protocol_params: &ProtocolParameters, -) -> Result<(), Error> { - if VERIFY { - verify_mana_allotments_unique_sorted(allotments)?; - verify_mana_allotments_sum(allotments, protocol_params)?; - } - - Ok(()) -} - -fn verify_mana_allotments_unique_sorted<'a>( - allotments: impl IntoIterator, -) -> Result<(), Error> { - if !is_unique_sorted(allotments.into_iter()) { - return Err(Error::ManaAllotmentsNotUniqueSorted); - } - Ok(()) -} - -pub(crate) fn verify_mana_allotments_sum<'a>( - allotments: impl IntoIterator, - protocol_params: &ProtocolParameters, -) -> Result<(), Error> { - let mut mana_sum: u64 = 0; - let max_mana = protocol_params.mana_parameters().max_mana(); - - for ManaAllotment { mana, .. } in allotments { - mana_sum = mana_sum.checked_add(*mana).ok_or(Error::InvalidManaAllotmentSum { - sum: mana_sum as u128 + *mana as u128, - max: max_mana, - })?; - - if mana_sum > max_mana { - return Err(Error::InvalidManaAllotmentSum { - sum: mana_sum as u128, - max: max_mana, - }); - } - } - - Ok(()) -} - -impl TryFrom> for ManaAllotments { - type Error = Error; - - #[inline(always)] - fn try_from(allotments: Vec) -> Result { - Self::from_vec(allotments) - } -} - -impl TryFrom> for ManaAllotments { - type Error = Error; - - #[inline(always)] - fn try_from(allotments: BTreeSet) -> Result { - Self::from_set(allotments) - } -} - -impl IntoIterator for ManaAllotments { - type Item = ManaAllotment; - type IntoIter = alloc::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - Vec::from(Into::>::into(self.0)).into_iter() - } -} +pub(crate) use self::allotment::{verify_mana_allotments_sum, ManaAllotmentCount}; +pub use self::{ + allotment::{ManaAllotment, ManaAllotments}, + parameters::ManaParameters, + rewards::RewardsParameters, +}; diff --git a/sdk/src/types/block/mana/parameters.rs b/sdk/src/types/block/mana/parameters.rs index e6d68aa698..925b57a574 100644 --- a/sdk/src/types/block/mana/parameters.rs +++ b/sdk/src/types/block/mana/parameters.rs @@ -84,6 +84,7 @@ impl ManaParameters { if self.generation_rate() == 0 || slot_delta == 0 { return 0; } + fixed_point_multiply( amount, slot_delta * self.generation_rate() as u32, @@ -121,12 +122,14 @@ impl ProtocolParameters { self.epoch_index_of(slot_index_created), self.epoch_index_of(slot_index_target), ); + if epoch_index_created > epoch_index_target { return Err(Error::InvalidEpochDelta { created: epoch_index_created, target: epoch_index_target, }); } + Ok(self .mana_parameters() .decay(mana, epoch_index_target.0 - epoch_index_created.0)) @@ -140,12 +143,14 @@ impl ProtocolParameters { claimed_epoch: impl Into, ) -> Result { let (reward_epoch, claimed_epoch) = (reward_epoch.into(), claimed_epoch.into()); + if reward_epoch > claimed_epoch { return Err(Error::InvalidEpochDelta { created: reward_epoch, target: claimed_epoch, }); } + Ok(self.mana_parameters().decay(reward, claimed_epoch.0 - reward_epoch.0)) } @@ -162,6 +167,7 @@ impl ProtocolParameters { self.epoch_index_of(slot_index_created), self.epoch_index_of(slot_index_target), ); + if epoch_index_created > epoch_index_target { return Err(Error::InvalidEpochDelta { created: epoch_index_created, @@ -171,6 +177,7 @@ impl ProtocolParameters { if slot_index_created >= slot_index_target { return Ok(0); } + let mana_parameters = self.mana_parameters(); Ok(if epoch_index_created == epoch_index_target {