diff --git a/packages/mana/balance.go b/packages/mana/balance.go index 8c36a628cefd738c3da55c351b857ba457124b8d..ebf746872358ab76fb954099f266174feb291e73 100644 --- a/packages/mana/balance.go +++ b/packages/mana/balance.go @@ -1,6 +1,8 @@ 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. // diff --git a/packages/mana/balance_test.go b/packages/mana/balance_test.go index e38b15150935c7e2c1ba0bbd0126066ef80dcf9d..0a35b1e6b862d41bf21bd9ce5db4811175dc83e1 100644 --- a/packages/mana/balance_test.go +++ b/packages/mana/balance_test.go @@ -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)) + } } diff --git a/packages/mana/transfer.go b/packages/mana/transfer.go index cb8da44dd9826b48f21dd23714f4ed09f4346890..bcf42d744083af21c330547b26040a0e0ad9b567 100644 --- a/packages/mana/transfer.go +++ b/packages/mana/transfer.go @@ -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, - } -}