package bundle import ( "encoding/binary" "strconv" "sync" "unsafe" "github.com/iotaledger/goshimmer/packages/bitutils" "github.com/iotaledger/goshimmer/packages/errors" "github.com/iotaledger/goshimmer/packages/typeutils" "github.com/iotaledger/iota.go/trinary" ) type Bundle struct { hash trinary.Trytes hashMutex sync.RWMutex transactionHashes []trinary.Trytes transactionHashesMutex sync.RWMutex isValueBundle bool isValueBundleMutex sync.RWMutex bundleEssenceHash trinary.Trytes bundleEssenceHashMutex sync.RWMutex modified bool modifiedMutex sync.RWMutex } func New(headTransactionHash trinary.Trytes) (result *Bundle) { result = &Bundle{ hash: headTransactionHash, } return } func (bundle *Bundle) GetHash() (result trinary.Trytes) { bundle.hashMutex.RLock() result = bundle.hash bundle.hashMutex.RUnlock() return } func (bundle *Bundle) SetHash(hash trinary.Trytes) { bundle.hashMutex.Lock() bundle.hash = hash bundle.hashMutex.Unlock() } func (bundle *Bundle) GetTransactionHashes() (result []trinary.Trytes) { bundle.bundleEssenceHashMutex.RLock() result = bundle.transactionHashes bundle.bundleEssenceHashMutex.RUnlock() return } func (bundle *Bundle) SetTransactionHashes(transactionHashes []trinary.Trytes) { bundle.transactionHashesMutex.Lock() bundle.transactionHashes = transactionHashes bundle.transactionHashesMutex.Unlock() } func (bundle *Bundle) IsValueBundle() (result bool) { bundle.isValueBundleMutex.RLock() result = bundle.isValueBundle bundle.isValueBundleMutex.RUnlock() return } func (bundle *Bundle) SetValueBundle(valueBundle bool) { bundle.isValueBundleMutex.Lock() bundle.isValueBundle = valueBundle bundle.isValueBundleMutex.Unlock() } func (bundle *Bundle) GetBundleEssenceHash() (result trinary.Trytes) { bundle.bundleEssenceHashMutex.RLock() result = bundle.bundleEssenceHash bundle.bundleEssenceHashMutex.RUnlock() return } func (bundle *Bundle) SetBundleEssenceHash(bundleEssenceHash trinary.Trytes) { bundle.bundleEssenceHashMutex.Lock() bundle.bundleEssenceHash = bundleEssenceHash bundle.bundleEssenceHashMutex.Unlock() } func (bundle *Bundle) GetModified() (result bool) { bundle.modifiedMutex.RLock() result = bundle.modified bundle.modifiedMutex.RUnlock() return } func (bundle *Bundle) SetModified(modified bool) { bundle.modifiedMutex.Lock() bundle.modified = modified bundle.modifiedMutex.Unlock() } func (bundle *Bundle) Marshal() (result []byte) { bundle.hashMutex.RLock() bundle.bundleEssenceHashMutex.RLock() bundle.isValueBundleMutex.RLock() bundle.transactionHashesMutex.RLock() result = make([]byte, MARSHALED_MIN_SIZE+len(bundle.transactionHashes)*MARSHALED_TRANSACTION_HASH_SIZE) binary.BigEndian.PutUint64(result[MARSHALED_TRANSACTIONS_COUNT_START:MARSHALED_TRANSACTIONS_COUNT_END], uint64(len(bundle.transactionHashes))) copy(result[MARSHALED_HASH_START:MARSHALED_HASH_END], typeutils.StringToBytes(bundle.hash)) copy(result[MARSHALED_BUNDLE_ESSENCE_HASH_START:MARSHALED_BUNDLE_ESSENCE_HASH_END], typeutils.StringToBytes(bundle.bundleEssenceHash)) var flags bitutils.BitMask if bundle.isValueBundle { flags = flags.SetFlag(0) } result[MARSHALED_FLAGS_START] = *(*byte)(unsafe.Pointer(&flags)) i := 0 for _, hash := range bundle.transactionHashes { var HASH_START = MARSHALED_APPROVERS_HASHES_START + i*(MARSHALED_TRANSACTION_HASH_SIZE) var HASH_END = HASH_START + MARSHALED_TRANSACTION_HASH_SIZE copy(result[HASH_START:HASH_END], typeutils.StringToBytes(hash)) i++ } bundle.transactionHashesMutex.RUnlock() bundle.isValueBundleMutex.RUnlock() bundle.bundleEssenceHashMutex.RUnlock() bundle.hashMutex.RUnlock() return } func (bundle *Bundle) Unmarshal(data []byte) (err errors.IdentifiableError) { dataLen := len(data) if dataLen < MARSHALED_MIN_SIZE { return ErrMarshallFailed.Derive(errors.New("unmarshall failed"), "marshaled bundle is too short") } hashesCount := binary.BigEndian.Uint64(data[MARSHALED_TRANSACTIONS_COUNT_START:MARSHALED_TRANSACTIONS_COUNT_END]) if dataLen < MARSHALED_MIN_SIZE+int(hashesCount)*MARSHALED_TRANSACTION_HASH_SIZE { return ErrMarshallFailed.Derive(errors.New("unmarshall failed"), "marshaled bundle is too short for "+strconv.FormatUint(hashesCount, 10)+" transactions") } bundle.hashMutex.Lock() bundle.bundleEssenceHashMutex.Lock() bundle.isValueBundleMutex.Lock() bundle.transactionHashesMutex.Lock() bundle.hash = trinary.Trytes(typeutils.BytesToString(data[MARSHALED_HASH_START:MARSHALED_HASH_END])) bundle.bundleEssenceHash = trinary.Trytes(typeutils.BytesToString(data[MARSHALED_BUNDLE_ESSENCE_HASH_START:MARSHALED_BUNDLE_ESSENCE_HASH_END])) flags := bitutils.BitMask(data[MARSHALED_FLAGS_START]) if flags.HasFlag(0) { bundle.isValueBundle = true } bundle.transactionHashes = make([]trinary.Trytes, hashesCount) for i := uint64(0); i < hashesCount; i++ { var HASH_START = MARSHALED_APPROVERS_HASHES_START + i*(MARSHALED_TRANSACTION_HASH_SIZE) var HASH_END = HASH_START + MARSHALED_TRANSACTION_HASH_SIZE bundle.transactionHashes[i] = trinary.Trytes(typeutils.BytesToString(data[HASH_START:HASH_END])) } bundle.transactionHashesMutex.Unlock() bundle.isValueBundleMutex.Unlock() bundle.bundleEssenceHashMutex.Unlock() bundle.hashMutex.Unlock() return }