Skip to content

Commit

Permalink
Revert "Update mana_decay_provider.go"
Browse files Browse the repository at this point in the history
This reverts commit e5f1445.
  • Loading branch information
muXxer committed Oct 19, 2023
1 parent 3644ddf commit 4407efd
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 10 deletions.
67 changes: 61 additions & 6 deletions mana_decay_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,59 @@ import (
"github.com/iotaledger/hive.go/lo"
)

// splitUint64 splits a uint64 value into two uint64 that hold the high and the low double-word.
func splitUint64(value uint64) (valueHi uint64, valueLo uint64) {
return value >> 32, value & 0x00000000FFFFFFFF
}

// mergeUint64 merges two uint64 values that hold the high and the low double-word into one uint64.
func mergeUint64(valueHi uint64, valueLo uint64) (value uint64) {
return (valueHi << 32) | valueLo
}

// fixedPointMultiplication32Splitted does a fixed point multiplication using two uint64
// containing the high and the low double-word of the value.
// ATTENTION: do not pass factor that use more than 32bits, otherwise this function overflows.
func fixedPointMultiplication32Splitted(valueHi uint64, valueLo uint64, factor uint64, scale uint64) (uint64, uint64, error) {
if scale > 32 {
return 0, 0, ierrors.Errorf("fixed point multiplication with a scaling factor >32 (%d) not allowed", scale)
}

// multiply the integer part of the fixed-point number by the factor
valueHi *= factor

// the lower 'scale' bits of the result are extracted and shifted left to form the lower part of the new fraction.
// the fractional part of the fixed-point number is multiplied by the factor and right-shifted by 'scale' bits.
// the sum of these two values forms the new lower part (valueLo) of the result.
valueLo = (valueHi&((1<<scale)-1))<<(32-scale) + (valueLo*factor)>>scale

// the right-shifted valueHi and the upper 32 bits of valueLo form the new higher part (valueHi) of the result.
valueHi = (valueHi >> scale) + (valueLo >> 32)

// the lower 32 bits of valueLo form the new lower part of the result.
valueLo &= 0x00000000FFFFFFFF

// return the result as a fixed-point number composed of two 64-bit integers
return valueHi, valueLo, nil
}

// fixedPointMultiplication32 does a fixed point multiplication.
func fixedPointMultiplication32(value uint64, factor uint64, scale uint64) (uint64, error) {
var remainingScale uint64
if scale > 32 {
remainingScale = scale - 32
scale = 32
}
valueHi, valueLo := splitUint64(value)

resultHi, resultLo, err := fixedPointMultiplication32Splitted(valueHi, valueLo, factor, scale)
if err != nil {
return 0, err
}

return mergeUint64(resultHi, resultLo) >> remainingScale, nil
}

// ManaDecayProvider calculates the mana decay and mana generation
// using fixed point arithmetic and a precomputed lookup table.
type ManaDecayProvider struct {
Expand Down Expand Up @@ -66,8 +119,8 @@ func (p *ManaDecayProvider) decay(value Mana, epochDiff EpochIndex) (Mana, error
return value, nil
}

result := uint64(value)
var err error
// split the value into two uint64 variables to prevent overflows
valueHi, valueLo := splitUint64(uint64(value))

// we keep applying the decay as long as epoch diffs are left
remainingEpochDiff := epochDiff
Expand All @@ -84,13 +137,15 @@ func (p *ManaDecayProvider) decay(value Mana, epochDiff EpochIndex) (Mana, error
decayFactor := p.decayFactors[diffsToDecay-1]

// apply the decay and scale the resulting value (fixed-point arithmetics)
result, err = safemath.Safe64MulShift(result, decayFactor, p.decayFactorsExponent)
var err error
valueHi, valueLo, err = fixedPointMultiplication32Splitted(valueHi, valueLo, decayFactor, p.decayFactorsExponent)
if err != nil {
return 0, ierrors.Wrap(err, "failed to calculate mana decay")
}
}

return Mana(result), nil
// combine both uint64 variables to get the actual value
return Mana(mergeUint64(valueHi, valueLo)), nil
}

// generateMana calculates the generated mana.
Expand All @@ -99,7 +154,7 @@ func (p *ManaDecayProvider) generateMana(value BaseToken, slotDiff SlotIndex) (M
return 0, nil
}

result, err := safemath.Safe64MulShift(uint64(value), uint64(slotDiff)*p.generationRate, p.generationRateExponent)
result, err := fixedPointMultiplication32(uint64(value), uint64(slotDiff)*p.generationRate, p.generationRateExponent)
if err != nil {
return 0, ierrors.Wrap(err, "failed to calculate mana generation")
}
Expand Down Expand Up @@ -164,7 +219,7 @@ func (p *ManaDecayProvider) ManaGenerationWithDecay(amount BaseToken, creationSl
return result, nil

default:
aux, err := safemath.Safe64MulShift(uint64(amount), p.decayFactorEpochsSum*p.generationRate, p.decayFactorEpochsSumExponent+p.generationRateExponent-p.slotsPerEpochExponent)
aux, err := fixedPointMultiplication32(uint64(amount), p.decayFactorEpochsSum*p.generationRate, p.decayFactorEpochsSumExponent+p.generationRateExponent-p.slotsPerEpochExponent)
if err != nil {
return 0, ierrors.Wrap(err, "failed to calculate auxiliary value")
}
Expand Down
8 changes: 4 additions & 4 deletions mana_decay_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,15 +243,15 @@ func TestManaDecay_PotentialMana(t *testing.T) {
amount: math.MaxInt64,
createdSlot: testTimeProvider.EpochStart(1),
targetSlot: testTimeProvider.EpochStart(iotago.EpochIndex(len(testManaDecayFactors) + 1)),
result: 183827294847826527,
result: 183827295065703076,
wantErr: nil,
},
{
name: "check if mana decay works for multiples of the available epochs in the lookup table",
amount: math.MaxInt64,
createdSlot: testTimeProvider.EpochStart(1),
targetSlot: testTimeProvider.EpochStart(iotago.EpochIndex(3*len(testManaDecayFactors) + 1)),
result: 410192222442040018,
result: 410192223115924783,
wantErr: nil,
},
{
Expand All @@ -275,15 +275,15 @@ func TestManaDecay_PotentialMana(t *testing.T) {
amount: math.MaxInt64,
createdSlot: testTimeProvider.EpochStart(1),
targetSlot: testTimeProvider.EpochEnd(3),
result: 1687319975062367,
result: 1687319824887185,
wantErr: nil,
},
{
name: "even with the highest possible int64 number, the calculation should not overflow",
amount: math.MaxInt64,
createdSlot: testTimeProvider.EpochStart(1),
targetSlot: testTimeProvider.EpochStart(401),
result: 190239292158065300,
result: 190239292388858706,
wantErr: nil,
},
}
Expand Down

0 comments on commit 4407efd

Please sign in to comment.