Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mana calculations #1376

Merged
merged 26 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 146 additions & 3 deletions sdk/src/types/block/mana/structure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
use getset::CopyGetters;
use packable::{prefix::BoxedSlicePrefix, Packable};

use crate::types::block::{slot::EpochIndex, Error};
use crate::types::block::{
protocol::ProtocolParameters,
slot::{EpochIndex, SlotIndex},
Error,
};

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Packable, CopyGetters)]
#[cfg_attr(
Expand Down Expand Up @@ -41,14 +45,53 @@ impl ManaStructure {
}

/// Returns the mana decay factor for the given epoch index.
pub fn decay_factor_at(&self, epoch_index: EpochIndex) -> Option<u32> {
self.decay_factors.get(*epoch_index as usize).copied()
pub fn decay_factor_at(&self, epoch_index: impl Into<EpochIndex>) -> Option<u32> {
self.decay_factors.get(*epoch_index.into() as usize).copied()
}

/// Returns the max mana that can exist with the mana bits defined.
pub fn max_mana(&self) -> u64 {
(1 << self.bits_count) - 1
}

pub fn decay(&self, mana: u64, epoch_delta: u64) -> u64 {
if mana == 0 || epoch_delta == 0 {
return mana;
}

// split the value into two u64 variables to prevent overflowing
let mut mana_hi = upper_bits(mana);
let mut mana_lo = lower_bits(mana);

// we keep applying the lookup table factors as long as n epochs are left
let mut remaining_epochs = epoch_delta;

while remaining_epochs > 0 {
let epochs_to_decay = remaining_epochs.min(self.decay_factors().len() as u64);
remaining_epochs -= epochs_to_decay;

// Unwrap: Safe because the index is at most the length
let decay_factor = self.decay_factor_at(epochs_to_decay - 1).unwrap();

// apply the decay using fixed-point arithmetics.
(mana_hi, mana_lo) =
multiplication_and_shift(mana_hi, mana_lo, decay_factor, self.decay_factors_exponent());
}
Alex6323 marked this conversation as resolved.
Show resolved Hide resolved

// combine both u64 variables to get the actual value
mana_hi << 32 | mana_lo
}

pub(crate) fn generate_mana(&self, amount: u64, slot_delta: u32) -> u64 {
if self.generation_rate() == 0 || slot_delta == 0 {
return 0;
}
fixed_point_multiply(
amount,
slot_delta * self.generation_rate() as u32,
self.generation_rate_exponent(),
)
}
}

impl Default for ManaStructure {
Expand All @@ -65,3 +108,103 @@ impl Default for ManaStructure {
}
}
}

impl ProtocolParameters {
/// Calculates the potential mana that is generated by holding `amount` tokens from `slot_index_created` to
/// `slot_index_target` and applies the decay to the result
pub fn potential_mana(&self, amount: u64, slot_index_created: SlotIndex, slot_index_target: SlotIndex) -> u64 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Into dyn index ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

k

if slot_index_created >= slot_index_target {
return 0;
}
let slots_per_epoch_exp = self.slots_per_epoch_exponent();
let mana_structure = self.mana_structure();
let (epoch_index_created, epoch_index_target) = (
slot_index_created.to_epoch_index(slots_per_epoch_exp),
slot_index_target.to_epoch_index(slots_per_epoch_exp),
);
if epoch_index_created == epoch_index_target {
mana_structure.generate_mana(amount, (*slot_index_target - *slot_index_created) as u32)
} else if epoch_index_created == epoch_index_target - 1 {
let slots_before_next_epoch =
slot_index_created - (epoch_index_created + 1).first_slot_index(slots_per_epoch_exp);
let slots_since_epoch_start =
slot_index_target - (epoch_index_target - 1).last_slot_index(slots_per_epoch_exp);
let mana_decayed = mana_structure.decay(
mana_structure.generate_mana(amount, slots_before_next_epoch.0 as u32),
1,
);
let mana_generated = mana_structure.generate_mana(amount, slots_since_epoch_start.0 as u32);
mana_decayed + mana_generated
} else {
let c = fixed_point_multiply(
amount,
mana_structure.decay_factor_epochs_sum() * mana_structure.generation_rate() as u32,
mana_structure.decay_factor_epochs_sum_exponent() + mana_structure.generation_rate_exponent()
- slots_per_epoch_exp,
);
let slots_before_next_epoch =
slot_index_created - (epoch_index_created + 1).first_slot_index(slots_per_epoch_exp);
let slots_since_epoch_start =
slot_index_target - (epoch_index_target - 1).last_slot_index(slots_per_epoch_exp);
let potential_mana_n = mana_structure.decay(
mana_structure.generate_mana(amount, slots_before_next_epoch.0 as u32),
epoch_index_target.0 - epoch_index_created.0,
);
let potential_mana_n_1 = mana_structure.decay(c, epoch_index_target.0 - epoch_index_created.0);
let potential_mana_0 = c + mana_structure.generate_mana(amount, slots_since_epoch_start.0 as u32)
- (c >> mana_structure.generation_rate_exponent());
potential_mana_0 - potential_mana_n_1 + potential_mana_n
}
}
}

/// Returns the upper 32 bits of a u64 value.
fn upper_bits(v: u64) -> u64 {
v >> 32
Alex6323 marked this conversation as resolved.
Show resolved Hide resolved
}

/// Returns the lower n bits of a u64 value.
fn lower_n_bits(v: u64, n: u8) -> u64 {
debug_assert!(n <= 64);
Alex6323 marked this conversation as resolved.
Show resolved Hide resolved
v & u64::MAX >> (64 - n)
}

/// Returns the lower 32 bits of a u64 value.
fn lower_bits(v: u64) -> u64 {
v & 0xFFFFFFFF
}

/// Returns the result of the multiplication ((value_hi << 32 + value_lo) * mult_factor) >> shift_factor
/// (where mult_factor is a uint32, value_hi and value_lo are uint64 smaller than 2^32, and 0 <= shift_factor <=
/// 32), using only uint64 multiplication functions, without overflowing. The returned result is split
/// in 2 factors: value_hi and value_lo, one containing the upper 32 bits of the result and the other
/// containing the lower 32 bits.
fn multiplication_and_shift(mut value_hi: u64, mut value_lo: u64, mult_factor: u32, shift_factor: u8) -> (u64, u64) {
debug_assert!(shift_factor <= 32);
// multiply the integer part of value_hi by mult_factor
value_hi = value_hi * mult_factor as u64;

// the lower shift_factor bits of the result are extracted and shifted left to form the remainder.
// value_lo is multiplied by mult_factor and right-shifted by shift_factor bits.
// the sum of these two values forms the new lower part (value_lo) of the result.
value_lo =
(lower_n_bits(value_hi, shift_factor) << (32 - shift_factor)) + (value_lo * mult_factor as u64) >> shift_factor;

// the right-shifted value_hi and the upper 32 bits of value_lo form the new higher part (value_hi) of the
// result.
value_hi = (value_hi >> shift_factor) + upper_bits(value_lo);

// the lower 32 bits of value_lo form the new lower part of the result.
value_lo = lower_bits(value_lo);

// return the result as a fixed-point number composed of two 64-bit integers
(value_hi, value_lo)
}

/// Wrapper for [`multiplication_and_shift`] that splits and re-combines the given value.
fn fixed_point_multiply(value: u64, mult_factor: u32, shift_factor: u8) -> u64 {
let value_hi = upper_bits(value);
let value_lo = lower_bits(value);
let (amount_hi, amount_lo) = multiplication_and_shift(value_hi, value_lo, mult_factor, shift_factor);
amount_hi << 32 | amount_lo
}
21 changes: 11 additions & 10 deletions sdk/src/types/block/output/feature/block_issuer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ impl Ed25519BlockIssuerKey {
/// The block issuer key kind of an [`Ed25519BlockIssuerKey`].
pub const KIND: u8 = 0;
/// Length of an ED25519 block issuer key.
pub const PUBLIC_KEY_LENGTH: usize = ed25519::PublicKey::LENGTH;
pub const LENGTH: usize = ed25519::PublicKey::LENGTH;

/// Creates a new [`Ed25519BlockIssuerKey`] from bytes.
pub fn try_from_bytes(bytes: [u8; Self::PUBLIC_KEY_LENGTH]) -> Result<Self, Error> {
pub fn try_from_bytes(bytes: [u8; Self::LENGTH]) -> Result<Self, Error> {
Ok(Self(ed25519::PublicKey::try_from_bytes(bytes)?))
}
}
Expand All @@ -94,7 +94,7 @@ impl Packable for Ed25519BlockIssuerKey {
unpacker: &mut U,
visitor: &Self::UnpackVisitor,
) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
Self::try_from_bytes(<[u8; Self::PUBLIC_KEY_LENGTH]>::unpack::<_, VERIFY>(unpacker, visitor).coerce()?)
Self::try_from_bytes(<[u8; Self::LENGTH]>::unpack::<_, VERIFY>(unpacker, visitor).coerce()?)
.map_err(UnpackError::Packable)
}
}
Expand Down Expand Up @@ -148,11 +148,11 @@ impl IntoIterator for BlockIssuerKeys {
}

impl BlockIssuerKeys {
/// The minimum number of block_issuer_keys in a [`BlockIssuerFeature`].
/// The minimum number of block issuer keys in a [`BlockIssuerFeature`].
pub const COUNT_MIN: u8 = 1;
/// The maximum number of block_issuer_keys in a [`BlockIssuerFeature`].
/// The maximum number of block issuer keys in a [`BlockIssuerFeature`].
pub const COUNT_MAX: u8 = 128;
/// The range of valid numbers of block_issuer_keys.
/// The range of valid numbers of block issuer keys.
pub const COUNT_RANGE: RangeInclusive<u8> = Self::COUNT_MIN..=Self::COUNT_MAX; // [1..128]

/// Creates a new [`BlockIssuerKeys`] from a vec.
Expand Down Expand Up @@ -186,9 +186,9 @@ impl BlockIssuerKeys {
#[derive(Clone, Debug, Eq, PartialEq, Hash, packable::Packable)]
#[packable(unpack_error = Error)]
pub struct BlockIssuerFeature {
/// The slot index at which the Block Issuer Feature expires and can be removed.
/// The slot index at which the feature expires and can be removed.
expiry_slot: SlotIndex,
/// The Block Issuer Keys.
/// The block issuer keys.
block_issuer_keys: BlockIssuerKeys,
}

Expand All @@ -204,18 +204,19 @@ impl BlockIssuerFeature {
) -> Result<Self, Error> {
let block_issuer_keys =
BlockIssuerKeys::from_vec(block_issuer_keys.into_iter().collect::<Vec<BlockIssuerKey>>())?;

Ok(Self {
expiry_slot: expiry_slot.into(),
block_issuer_keys,
})
}

/// Returns the Slot Index at which the Block Issuer Feature expires and can be removed.
/// Returns the expiry slot.
pub fn expiry_slot(&self) -> SlotIndex {
self.expiry_slot
}

/// Returns the Block Issuer Keys.
/// Returns the block issuer keys.
pub fn block_issuer_keys(&self) -> &[BlockIssuerKey] {
&self.block_issuer_keys
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/types/block/rand/slot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ pub fn rand_slot_commitment_id() -> SlotCommitmentId {

/// Generates a random slot index.
pub fn rand_slot_index() -> SlotIndex {
SlotIndex::new(rand_number())
SlotIndex(rand_number())
}
Loading