diff --git a/packages/model/approvers/approvers.go b/packages/model/approvers/approvers.go index be5f753073d7adc2b1f1b9b754aa8fb957114253..f17c4ca36ecbdf5b292b7060f280c6122a734472 100644 --- a/packages/model/approvers/approvers.go +++ b/packages/model/approvers/approvers.go @@ -1,9 +1,13 @@ package approvers import ( + "encoding/binary" + "strconv" "sync" + "github.com/iotaledger/goshimmer/packages/errors" "github.com/iotaledger/goshimmer/packages/ternary" + "github.com/iotaledger/goshimmer/packages/typeutils" ) type Approvers struct { @@ -20,3 +24,119 @@ func NewApprovers(hash ternary.Trinary) *Approvers { modified: false, } } + +// region public methods with locking ////////////////////////////////////////////////////////////////////////////////// + +func (approvers *Approvers) Add(transactionHash ternary.Trinary) { + approvers.hashesMutex.Lock() + approvers.add(transactionHash) + approvers.hashesMutex.Unlock() +} + +func (approvers *Approvers) Remove(approverHash ternary.Trinary) { + approvers.hashesMutex.Lock() + approvers.remove(approverHash) + approvers.hashesMutex.Unlock() +} + +func (approvers *Approvers) GetHashes() (result []ternary.Trinary) { + approvers.hashesMutex.RLock() + result = approvers.getHashes() + approvers.hashesMutex.RUnlock() + + return +} + +func (approvers *Approvers) GetHash() (result ternary.Trinary) { + approvers.hashesMutex.RLock() + result = approvers.hash + approvers.hashesMutex.RUnlock() + + return +} + +func (approvers *Approvers) Marshal() (result []byte) { + result = make([]byte, MARSHALED_APPROVERS_MIN_SIZE+len(approvers.hashes)*MARSHALED_APPROVERS_HASH_SIZE) + + approvers.hashesMutex.RLock() + + binary.BigEndian.PutUint64(result[MARSHALED_APPROVERS_HASHES_COUNT_START:MARSHALED_APPROVERS_HASHES_COUNT_END], uint64(len(approvers.hashes))) + + copy(result[MARSHALED_APPROVERS_HASH_START:MARSHALED_APPROVERS_HASH_END], approvers.hash.CastToBytes()) + + i := 0 + for hash := range approvers.hashes { + var HASH_START = MARSHALED_APPROVERS_HASHES_START + i*(MARSHALED_APPROVERS_HASH_SIZE) + var HASH_END = HASH_START * MARSHALED_APPROVERS_HASH_SIZE + + copy(result[HASH_START:HASH_END], hash.CastToBytes()) + + i++ + } + + approvers.hashesMutex.RUnlock() + + return +} + +func (approvers *Approvers) Unmarshal(data []byte) (err errors.IdentifiableError) { + dataLen := len(data) + + if dataLen <= MARSHALED_APPROVERS_MIN_SIZE { + return ErrMarshallFailed.Derive(errors.New("unmarshall failed"), "marshaled approvers are too short") + } + + hashesCount := binary.BigEndian.Uint64(data[MARSHALED_APPROVERS_HASHES_COUNT_START:MARSHALED_APPROVERS_HASHES_COUNT_END]) + + if dataLen <= MARSHALED_APPROVERS_MIN_SIZE+int(hashesCount)*MARSHALED_APPROVERS_HASH_SIZE { + return ErrMarshallFailed.Derive(errors.New("unmarshall failed"), "marshaled approvers are too short for "+strconv.FormatUint(hashesCount, 10)+" approvers") + } + + approvers.hashesMutex.Lock() + + approvers.hash = ternary.Trinary(typeutils.BytesToString(data[MARSHALED_APPROVERS_HASH_START:MARSHALED_APPROVERS_HASH_END])) + approvers.hashes = make(map[ternary.Trinary]bool, hashesCount) + for i := uint64(0); i < hashesCount; i++ { + var HASH_START = MARSHALED_APPROVERS_HASHES_START + i*(MARSHALED_APPROVERS_HASH_SIZE) + var HASH_END = HASH_START * MARSHALED_APPROVERS_HASH_SIZE + + approvers.hashes[ternary.Trinary(typeutils.BytesToString(data[HASH_START:HASH_END]))] = true + } + + approvers.hashesMutex.Unlock() + + return +} + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// region private methods without locking ////////////////////////////////////////////////////////////////////////////// + +func (approvers *Approvers) add(transactionHash ternary.Trinary) { + if _, exists := approvers.hashes[transactionHash]; !exists { + approvers.hashes[transactionHash] = true + approvers.modified = true + } +} + +func (approvers *Approvers) remove(approverHash ternary.Trinary) { + if _, exists := approvers.hashes[approverHash]; exists { + delete(approvers.hashes, approverHash) + approvers.modified = true + } +} + +func (approvers *Approvers) getHashes() (result []ternary.Trinary) { + result = make([]ternary.Trinary, len(approvers.hashes)) + + counter := 0 + for hash := range approvers.hashes { + result[counter] = hash + + counter++ + } + + return +} + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/model/approvers/constants.go b/packages/model/approvers/constants.go new file mode 100644 index 0000000000000000000000000000000000000000..9c777856affc38393feed697e40986d33afd46b9 --- /dev/null +++ b/packages/model/approvers/constants.go @@ -0,0 +1,14 @@ +package approvers + +const ( + MARSHALED_APPROVERS_HASHES_COUNT_START = 0 + MARSHALED_APPROVERS_HASH_START = MARSHALED_APPROVERS_HASHES_COUNT_END + MARSHALED_APPROVERS_HASHES_START = MARSHALED_APPROVERS_HASH_END + + MARSHALED_APPROVERS_HASHES_COUNT_END = MARSHALED_APPROVERS_HASHES_COUNT_START + MARSHALED_APPROVERS_HASHES_COUNT_SIZE + MARSHALED_APPROVERS_HASH_END = MARSHALED_APPROVERS_HASH_START + MARSHALED_APPROVERS_HASH_SIZE + + MARSHALED_APPROVERS_HASHES_COUNT_SIZE = 8 + MARSHALED_APPROVERS_HASH_SIZE = 81 + MARSHALED_APPROVERS_MIN_SIZE = MARSHALED_APPROVERS_HASHES_COUNT_SIZE + MARSHALED_APPROVERS_HASH_SIZE +)