transaction_metadata.go 9.06 KiB
package tangle
import (
"github.com/iotaledger/goshimmer/packages/bitutils"
"github.com/iotaledger/goshimmer/packages/errors"
"github.com/iotaledger/goshimmer/packages/ternary"
"github.com/iotaledger/goshimmer/packages/typeconversion"
"sync"
"time"
)
// region type definition and constructor //////////////////////////////////////////////////////////////////////////////
type TransactionMetadata struct {
hash ternary.Trinary
hashMutex sync.RWMutex
receivedTime time.Time
receivedTimeMutex sync.RWMutex
solid bool
solidMutex sync.RWMutex
liked bool
likedMutex sync.RWMutex
finalized bool
finalizedMutex sync.RWMutex
modified bool
modifiedMutex sync.RWMutex
}
func NewTransactionMetadata(hash ternary.Trinary) *TransactionMetadata {
return &TransactionMetadata{
hash: hash,
receivedTime: time.Now(),
solid: false,
liked: false,
finalized: false,
modified: true,
}
}
// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
// region getters and setters //////////////////////////////////////////////////////////////////////////////////////////
func (metaData *TransactionMetadata) GetHash() ternary.Trinary {
metaData.hashMutex.RLock()
defer metaData.hashMutex.RUnlock()
return metaData.hash
}
func (metaData *TransactionMetadata) SetHash(hash ternary.Trinary) {
metaData.hashMutex.RLock()
if metaData.hash != hash {
metaData.hashMutex.RUnlock()
metaData.hashMutex.Lock()
defer metaData.hashMutex.Unlock()
if metaData.hash != hash {
metaData.hash = hash
metaData.SetModified(true)
}
} else {
metaData.hashMutex.RUnlock()
}
}
func (metaData *TransactionMetadata) GetReceivedTime() time.Time {
metaData.receivedTimeMutex.RLock()
defer metaData.receivedTimeMutex.RUnlock()
return metaData.receivedTime
}
func (metaData *TransactionMetadata) SetReceivedTime(receivedTime time.Time) {
metaData.receivedTimeMutex.RLock()
if metaData.receivedTime != receivedTime {
metaData.receivedTimeMutex.RUnlock()
metaData.receivedTimeMutex.Lock()
defer metaData.receivedTimeMutex.Unlock()
if metaData.receivedTime != receivedTime {
metaData.receivedTime = receivedTime
metaData.SetModified(true)
}
} else {
metaData.receivedTimeMutex.RUnlock()
}
}
func (metaData *TransactionMetadata) GetSolid() bool {
metaData.solidMutex.RLock()
defer metaData.solidMutex.RUnlock()
return metaData.solid
}
func (metaData *TransactionMetadata) SetSolid(solid bool) {
metaData.solidMutex.RLock()
if metaData.solid != solid {
metaData.solidMutex.RUnlock()
metaData.solidMutex.Lock()
defer metaData.solidMutex.Unlock()
if metaData.solid != solid {
metaData.solid = solid
metaData.SetModified(true)
}
} else {
metaData.solidMutex.RUnlock()
}
}
func (metaData *TransactionMetadata) GetLiked() bool {
metaData.likedMutex.RLock()
defer metaData.likedMutex.RUnlock()
return metaData.liked
}
func (metaData *TransactionMetadata) SetLiked(liked bool) {
metaData.likedMutex.RLock()
if metaData.liked != liked {
metaData.likedMutex.RUnlock()
metaData.likedMutex.Lock()
defer metaData.likedMutex.Unlock()
if metaData.liked != liked {
metaData.liked = liked
metaData.SetModified(true)
}
} else {
metaData.likedMutex.RUnlock()
}
}
func (metaData *TransactionMetadata) GetFinalized() bool {
metaData.finalizedMutex.RLock()
defer metaData.finalizedMutex.RUnlock()
return metaData.finalized
}
func (metaData *TransactionMetadata) SetFinalized(finalized bool) {
metaData.finalizedMutex.RLock()
if metaData.finalized != finalized {
metaData.finalizedMutex.RUnlock()
metaData.finalizedMutex.Lock()
defer metaData.finalizedMutex.Unlock()
if metaData.finalized != finalized {
metaData.finalized = finalized
metaData.SetModified(true)
}
} else {
metaData.finalizedMutex.RUnlock()
}
}
// returns true if the transaction contains unsaved changes (supports concurrency)
func (metadata *TransactionMetadata) GetModified() bool {
metadata.modifiedMutex.RLock()
defer metadata.modifiedMutex.RUnlock()
return metadata.modified
}
// sets the modified flag which controls if a transaction is going to be saved (supports concurrency)
func (metadata *TransactionMetadata) SetModified(modified bool) {
metadata.modifiedMutex.Lock()
defer metadata.modifiedMutex.Unlock()
metadata.modified = modified
}
// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
// region marshalling functions ////////////////////////////////////////////////////////////////////////////////////////
func (metadata *TransactionMetadata) Marshal() ([]byte, errors.IdentifiableError) {
marshalledMetadata := make([]byte, MARSHALLED_TOTAL_SIZE)
metadata.receivedTimeMutex.RLock()
defer metadata.receivedTimeMutex.RUnlock()
metadata.solidMutex.RLock()
defer metadata.solidMutex.RUnlock()
metadata.likedMutex.RLock()
defer metadata.likedMutex.RUnlock()
metadata.finalizedMutex.RLock()
defer metadata.finalizedMutex.RUnlock()
copy(marshalledMetadata[MARSHALLED_HASH_START:MARSHALLED_HASH_END], metadata.hash.CastToBytes())
marshalledReceivedTime, err := metadata.receivedTime.MarshalBinary()
if err != nil {
return nil, ErrMarshallFailed.Derive(err, "failed to marshal received time")
}
copy(marshalledMetadata[MARSHALLED_RECEIVED_TIME_START:MARSHALLED_RECEIVED_TIME_END], marshalledReceivedTime)
var booleanFlags bitutils.BitMask
if metadata.solid {
booleanFlags = booleanFlags.SetFlag(0)
}
if metadata.liked {
booleanFlags = booleanFlags.SetFlag(1)
}
if metadata.finalized {
booleanFlags = booleanFlags.SetFlag(2)
}
marshalledMetadata[MARSHALLED_FLAGS_START] = byte(booleanFlags)
return marshalledMetadata, nil
}
func (metadata *TransactionMetadata) Unmarshal(data []byte) errors.IdentifiableError {
metadata.hashMutex.Lock()
defer metadata.hashMutex.Unlock()
metadata.receivedTimeMutex.Lock()
defer metadata.receivedTimeMutex.Unlock()
metadata.solidMutex.Lock()
defer metadata.solidMutex.Unlock()
metadata.likedMutex.Lock()
defer metadata.likedMutex.Unlock()
metadata.finalizedMutex.Lock()
defer metadata.finalizedMutex.Unlock()
metadata.hash = ternary.Trinary(typeconversion.BytesToString(data[MARSHALLED_HASH_START:MARSHALLED_HASH_END]))
if err := metadata.receivedTime.UnmarshalBinary(data[MARSHALLED_RECEIVED_TIME_START:MARSHALLED_RECEIVED_TIME_END]); err != nil {
return ErrUnmarshalFailed.Derive(err, "could not unmarshal the received time")
}
booleanFlags := bitutils.BitMask(data[MARSHALLED_FLAGS_START])
if booleanFlags.HasFlag(0) {
metadata.solid = true
}
if booleanFlags.HasFlag(1) {
metadata.liked = true
}
if booleanFlags.HasFlag(2) {
metadata.finalized = true
}
return nil
}
// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
// region database functions ///////////////////////////////////////////////////////////////////////////////////////////
func (metadata *TransactionMetadata) Store() errors.IdentifiableError {
if metadata.GetModified() {
marshalledMetadata, err := metadata.Marshal()
if err != nil {
return err
}
if err := transactionMetadataDatabase.Set(metadata.GetHash().CastToBytes(), marshalledMetadata); err != nil {
return ErrDatabaseError.Derive(err, "failed to store the transaction")
}
metadata.SetModified(false)
}
return nil
}
// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
// region constants and variables //////////////////////////////////////////////////////////////////////////////////////
const (
MARSHALLED_HASH_START = 0
MARSHALLED_RECEIVED_TIME_START = MARSHALLED_HASH_END
MARSHALLED_FLAGS_START = MARSHALLED_RECEIVED_TIME_END
MARSHALLED_HASH_END = MARSHALLED_HASH_START + MARSHALLED_HASH_SIZE
MARSHALLED_RECEIVED_TIME_END = MARSHALLED_RECEIVED_TIME_START + MARSHALLED_RECEIVED_TIME_SIZE
MARSHALLED_FLAGS_END = MARSHALLED_FLAGS_START + MARSHALLED_FLAGS_SIZE
MARSHALLED_HASH_SIZE = 81
MARSHALLED_RECEIVED_TIME_SIZE = 15
MARSHALLED_FLAGS_SIZE = 1
MARSHALLED_TOTAL_SIZE = MARSHALLED_FLAGS_END
)
// endregion ////////////////////////////////////////////////////////////////////////////////////////////////////////////