Skip to content
Snippets Groups Projects
Commit b26a2aa1 authored by Hans Moog's avatar Hans Moog
Browse files

Feat: made balance thread safe + added history cleanup

parent 2a0b2609
No related branches found
No related tags found
No related merge requests found
package mana
import (
"sync"
"github.com/iotaledger/goshimmer/packages/datastructure"
"github.com/iotaledger/goshimmer/packages/errors"
)
......@@ -8,6 +10,7 @@ import (
type Balance struct {
calculator *Calculator
transferHistory *datastructure.DoublyLinkedList
mutex sync.RWMutex
}
func NewBalance(calculator *Calculator) *Balance {
......@@ -19,6 +22,9 @@ func NewBalance(calculator *Calculator) *Balance {
// Returns the current mana balance.
func (balance *Balance) GetValue(now ...uint64) (result uint64, err errors.IdentifiableError) {
balance.mutex.RLock()
defer balance.mutex.RUnlock()
if lastBalanceHistoryEntry, historyErr := balance.transferHistory.GetLast(); historyErr != nil {
if !datastructure.ErrNoSuchElement.Equals(historyErr) {
err = historyErr
......@@ -46,6 +52,9 @@ func (balance *Balance) GetValue(now ...uint64) (result uint64, err errors.Ident
// Returns the timestamp of the last mana erosion.
func (balance *Balance) GetLastErosion() uint64 {
balance.mutex.RLock()
defer balance.mutex.RUnlock()
if lastBalanceHistoryEntry, err := balance.transferHistory.GetLast(); datastructure.ErrNoSuchElement.Equals(err) {
return 0
} else {
......@@ -54,7 +63,10 @@ func (balance *Balance) GetLastErosion() uint64 {
}
// Books a new transfer to the balance.
func (balance *Balance) AddTransfer(transfer *Transfer) {
func (balance *Balance) BookTransfer(transfer *Transfer) {
balance.mutex.Lock()
defer balance.mutex.Unlock()
// check if we need to rollback transfers (to prevent rounding errors)
rolledBackTransactions := balance.rollbackTransfers(transfer.spentTime)
......@@ -67,6 +79,32 @@ func (balance *Balance) AddTransfer(transfer *Transfer) {
}
}
// Cleans up old transfer history entries to reduce the size of the data.
func (balance *Balance) CleanupTransferHistory(referenceTime uint64) (err errors.IdentifiableError) {
balance.mutex.Lock()
defer balance.mutex.Unlock()
if currentTransferHistoryEntry, historyErr := balance.transferHistory.GetFirstEntry(); historyErr != nil {
if !datastructure.ErrNoSuchElement.Equals(historyErr) {
err = historyErr
}
} else {
for currentTransferHistoryEntry.GetNext() != nil && currentTransferHistoryEntry.GetValue().(*BalanceHistoryEntry).transfer.spentTime < referenceTime {
nextTransferHistoryEntry := currentTransferHistoryEntry.GetNext()
if historyErr := balance.transferHistory.RemoveEntry(currentTransferHistoryEntry); historyErr != nil {
err = historyErr
break
}
currentTransferHistoryEntry = nextTransferHistoryEntry
}
}
return
}
// Rolls back transfers that have their spentTime after the given referenceTime and returns a slice containing the
// rolled back transfers.
//
......
......@@ -6,24 +6,68 @@ import (
"github.com/magiconair/properties/assert"
)
func TestBalance_CleanupTransferHistory(t *testing.T) {
// initialize calculator
calculator := NewCalculator(500, 0.1)
// fill transfer history
balance1 := NewBalance(calculator)
balance1.BookTransfer(&Transfer{
movedCoins: 1000,
burnedMana: 10,
receivedTime: 1000,
spentTime: 1700,
})
balance1.BookTransfer(&Transfer{
movedCoins: 1000,
burnedMana: 0,
receivedTime: 700,
spentTime: 1000,
})
balance1.BookTransfer(&Transfer{
movedCoins: 1000,
burnedMana: 0,
receivedTime: 0,
spentTime: 700,
})
// cleanup transfer history
if err := balance1.CleanupTransferHistory(1900); err != nil {
t.Error(err)
return
}
// check result (correct balance, correct history size)
if val1, err := balance1.GetValue(); err != nil {
t.Error(err)
return
} else {
assert.Equal(t, val1, uint64(290))
}
assert.Equal(t, balance1.transferHistory.GetSize(), 1)
}
func TestBalance_AddTransfer(t *testing.T) {
// initialize calculator
calculator := NewCalculator(500, 0.1)
// spend coins multiple times
balance1 := NewBalance(calculator)
balance1.AddTransfer(&Transfer{
balance1.BookTransfer(&Transfer{
movedCoins: 1000,
burnedMana: 10,
receivedTime: 1000,
spentTime: 1700,
})
balance1.AddTransfer(&Transfer{
balance1.BookTransfer(&Transfer{
movedCoins: 1000,
burnedMana: 0,
receivedTime: 700,
spentTime: 1000,
})
balance1.AddTransfer(&Transfer{
balance1.BookTransfer(&Transfer{
movedCoins: 1000,
burnedMana: 0,
receivedTime: 0,
......@@ -32,7 +76,7 @@ func TestBalance_AddTransfer(t *testing.T) {
// hold coins for the full time
balance2 := NewBalance(calculator)
balance2.AddTransfer(&Transfer{
balance2.BookTransfer(&Transfer{
movedCoins: 1000,
burnedMana: 10,
receivedTime: 0,
......@@ -40,8 +84,18 @@ func TestBalance_AddTransfer(t *testing.T) {
})
// check result
val1, _ := balance1.GetValue()
assert.Equal(t, val1, uint64(291))
val2, _ := balance2.GetValue()
assert.Equal(t, val2, uint64(291))
if val1, err := balance1.GetValue(); err != nil {
t.Error(err)
return
} else {
assert.Equal(t, val1, uint64(290))
}
if val2, err := balance2.GetValue(); err != nil {
t.Error(err)
return
} else {
assert.Equal(t, val2, uint64(291))
}
}
......@@ -6,12 +6,3 @@ type Transfer struct {
receivedTime uint64
spentTime uint64
}
func NewTransfer(movedCoins uint64, burnedMana uint64, receivedTime uint64, spentTime uint64) *Transfer {
return &Transfer{
movedCoins: movedCoins,
burnedMana: burnedMana,
receivedTime: receivedTime,
spentTime: spentTime,
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment