Skip to content
Snippets Groups Projects
Select Git revision
  • 5139b499498f98552aa3ff4ec282a2d1b3d39c30
  • without_tipselection default
  • develop protected
  • fix/grafana-local-dashboard
  • wasp
  • fix/dashboard-explorer-freeze
  • master
  • feat/timerqueue
  • test/sync_debug_and_650
  • feat/sync_revamp_inv
  • wip/sync
  • tool/db-recovery
  • portcheck/fix
  • fix/synchronization
  • feat/new-dashboard-analysis
  • feat/refactored-analysis-dashboard
  • feat/new-analysis-dashboard
  • test/demo-prometheus-fpc
  • prometheus_metrics
  • wip/analysis-server
  • merge/fpc-test-value-transfer
  • v0.2.2
  • v0.2.1
  • v0.2.0
  • v0.1.3
  • v0.1.2
  • v0.1.1
  • v0.1.0
28 results

transfer_output.go

Blame
  • user avatar
    Hans Moog authored
    5139b499
    History
    transfer_output.go 8.56 KiB
    package ledgerstate
    
    import (
    	"encoding/binary"
    	"sync"
    
    	"github.com/iotaledger/goshimmer/packages/stringify"
    	"github.com/iotaledger/hive.go/objectstorage"
    )
    
    type TransferOutput struct {
    	transferHash TransferHash
    	addressHash  AddressHash
    	balances     []*ColoredBalance
    	realityId    RealityId
    	consumers    map[TransferHash][]AddressHash
    
    	storageKey  []byte
    	ledgerState *LedgerState
    
    	realityIdMutex sync.RWMutex
    	consumersMutex sync.RWMutex
    	bookingMutex   sync.Mutex
    }
    
    func NewTransferOutput(ledgerState *LedgerState, realityId RealityId, transferHash TransferHash, addressHash AddressHash, balances ...*ColoredBalance) *TransferOutput {
    	return &TransferOutput{
    		transferHash: transferHash,
    		addressHash:  addressHash,
    		balances:     balances,
    		realityId:    realityId,
    		consumers:    make(map[TransferHash][]AddressHash),
    
    		storageKey:  append(transferHash[:], addressHash[:]...),
    		ledgerState: ledgerState,
    	}
    }
    
    func (transferOutput *TransferOutput) GetTransferHash() (transferHash TransferHash) {
    	transferHash = transferOutput.transferHash
    
    	return
    }
    
    func (transferOutput *TransferOutput) GetRealityId() (realityId RealityId) {
    	transferOutput.realityIdMutex.RLock()
    	realityId = transferOutput.realityId
    	transferOutput.realityIdMutex.RUnlock()
    
    	return
    }
    
    func (transferOutput *TransferOutput) GetAddressHash() (addressHash AddressHash) {
    	return transferOutput.addressHash
    }
    
    func (transferOutput *TransferOutput) SetRealityId(realityId RealityId) {
    	transferOutput.realityIdMutex.Lock()
    	transferOutput.realityId = realityId
    	transferOutput.realityIdMutex.Unlock()
    }
    
    func (transferOutput *TransferOutput) GetBalances() []*ColoredBalance {
    	return transferOutput.balances
    }
    
    func (transferOutput *TransferOutput) GetConsumers() (consumers map[TransferHash][]AddressHash) {
    	consumers = make(map[TransferHash][]AddressHash)
    
    	transferOutput.consumersMutex.RLock()
    	for transferHash, addresses := range transferOutput.consumers {
    		consumers[transferHash] = make([]AddressHash, len(addresses))
    		copy(consumers[transferHash], addresses)
    	}
    	transferOutput.consumersMutex.RUnlock()
    
    	return
    }
    
    func (transferOutput *TransferOutput) addConsumer(consumer TransferHash, outputs map[AddressHash][]*ColoredBalance) (consumersToElevate map[TransferHash][]AddressHash, err error) {
    	transferOutput.consumersMutex.RLock()
    	if _, exist := transferOutput.consumers[consumer]; exist {
    		transferOutput.consumersMutex.RUnlock()
    	} else {
    		transferOutput.consumersMutex.RUnlock()
    
    		transferOutput.consumersMutex.Lock()
    		switch len(transferOutput.consumers) {
    		case 0:
    			consumersToElevate = nil
    			err = transferOutput.markAsSpent()
    		case 1:
    			consumersToElevate = make(map[TransferHash][]AddressHash, 1)
    			for transferHash, addresses := range transferOutput.consumers {
    				consumersToElevate[transferHash] = addresses
    			}
    			err = nil
    		default:
    			consumersToElevate = make(map[TransferHash][]AddressHash)
    			err = nil
    		}
    		consumers := make([]AddressHash, len(outputs))
    		i := 0
    		for addressHash := range outputs {
    			consumers[i] = addressHash
    
    			i++
    		}
    
    		transferOutput.consumers[consumer] = consumers
    		transferOutput.consumersMutex.Unlock()
    	}
    
    	return
    }
    
    func (transferOutput *TransferOutput) markAsSpent() error {
    	transferOutput.bookingMutex.Lock()
    
    	currentBookingKey := generateTransferOutputBookingStorageKey(transferOutput.GetRealityId(), transferOutput.addressHash, false, transferOutput.transferHash)
    	if oldTransferOutputBooking, err := transferOutput.ledgerState.transferOutputBookings.Load(currentBookingKey); err != nil {
    		transferOutput.bookingMutex.Unlock()
    
    		return err
    	} else {
    		transferOutput.ledgerState.storeTransferOutputBooking(newTransferOutputBooking(transferOutput.GetRealityId(), transferOutput.addressHash, true, transferOutput.transferHash)).Release()
    
    		oldTransferOutputBooking.Delete().Release()
    	}
    
    	transferOutput.bookingMutex.Unlock()
    
    	return nil
    }
    
    func (transferOutput *TransferOutput) String() string {
    	return stringify.Struct("TransferOutput",
    		stringify.StructField("transferHash", transferOutput.transferHash.String()),
    		stringify.StructField("addressHash", transferOutput.addressHash.String()),
    		stringify.StructField("balances", transferOutput.balances),
    		stringify.StructField("realityId", transferOutput.realityId.String()),
    		stringify.StructField("spent", len(transferOutput.consumers) >= 1),
    	)
    }
    
    // region support object storage ///////////////////////////////////////////////////////////////////////////////////////
    
    func (transferOutput *TransferOutput) GetStorageKey() []byte {
    	return transferOutput.storageKey
    }
    
    func (transferOutput *TransferOutput) Update(other objectstorage.StorableObject) {}
    
    func (transferOutput *TransferOutput) MarshalBinary() ([]byte, error) {
    	transferOutput.realityIdMutex.RLock()
    	transferOutput.consumersMutex.RLock()
    
    	balanceCount := len(transferOutput.balances)
    	consumerCount := len(transferOutput.consumers)
    
    	serializedLength := realityIdLength + 4 + balanceCount*coloredBalanceLength + 4 + consumerCount*transferHashLength
    	for _, addresses := range transferOutput.consumers {
    		serializedLength += 4
    		for range addresses {
    			serializedLength += addressHashLength
    		}
    	}
    
    	result := make([]byte, serializedLength)
    
    	copy(result[0:], transferOutput.realityId[:])
    
    	binary.LittleEndian.PutUint32(result[realityIdLength:], uint32(balanceCount))
    	for i := 0; i < balanceCount; i++ {
    		copy(result[realityIdLength+4+i*coloredBalanceLength:], transferOutput.balances[i].color[:colorLength])
    		binary.LittleEndian.PutUint64(result[realityIdLength+4+i*coloredBalanceLength+colorLength:], transferOutput.balances[i].balance)
    	}
    	offset := realityIdLength + 4 + balanceCount*coloredBalanceLength
    
    	binary.LittleEndian.PutUint32(result[offset:], uint32(consumerCount))
    	offset += 4
    	for transferHash, addresses := range transferOutput.consumers {
    		copy(result[offset:], transferHash[:transferHashLength])
    		offset += transferHashLength
    
    		binary.LittleEndian.PutUint32(result[offset:], uint32(len(addresses)))
    		offset += 4
    
    		for _, addressHash := range addresses {
    			copy(result[offset:], addressHash[:addressHashLength])
    			offset += addressHashLength
    
    		}
    	}
    
    	transferOutput.consumersMutex.RUnlock()
    	transferOutput.realityIdMutex.RUnlock()
    
    	return result, nil
    }
    
    func (transferOutput *TransferOutput) UnmarshalBinary(serializedObject []byte) error {
    	if err := transferOutput.transferHash.UnmarshalBinary(transferOutput.storageKey[:transferHashLength]); err != nil {
    		return err
    	}
    
    	if err := transferOutput.addressHash.UnmarshalBinary(transferOutput.storageKey[transferHashLength:]); err != nil {
    		return err
    	}
    
    	if err := transferOutput.realityId.UnmarshalBinary(serializedObject[:realityIdLength]); err != nil {
    		return err
    	}
    
    	if balances, err := transferOutput.unmarshalBalances(serializedObject[realityIdLength:]); err != nil {
    		return err
    	} else {
    		transferOutput.balances = balances
    	}
    
    	if consumers, err := transferOutput.unmarshalConsumers(serializedObject[realityIdLength+4+len(transferOutput.balances)*coloredBalanceLength:]); err != nil {
    		return err
    	} else {
    		transferOutput.consumers = consumers
    	}
    
    	return nil
    }
    
    func (transferOutput *TransferOutput) unmarshalBalances(serializedBalances []byte) ([]*ColoredBalance, error) {
    	balanceCount := int(binary.LittleEndian.Uint32(serializedBalances))
    
    	balances := make([]*ColoredBalance, balanceCount)
    	for i := 0; i < balanceCount; i++ {
    		coloredBalance := ColoredBalance{}
    		if err := coloredBalance.UnmarshalBinary(serializedBalances[4+i*coloredBalanceLength:]); err != nil {
    			return nil, err
    		}
    
    		balances[i] = &coloredBalance
    	}
    
    	return balances, nil
    }
    
    func (transferOutput *TransferOutput) unmarshalConsumers(serializedConsumers []byte) (map[TransferHash][]AddressHash, error) {
    	offset := 0
    
    	consumerCount := int(binary.LittleEndian.Uint32(serializedConsumers[offset:]))
    	offset += 4
    
    	consumers := make(map[TransferHash][]AddressHash, consumerCount)
    	for i := 0; i < consumerCount; i++ {
    		transferHash := TransferHash{}
    		if err := transferHash.UnmarshalBinary(serializedConsumers[offset:]); err != nil {
    			return nil, err
    		}
    		offset += transferHashLength
    
    		addressHashCount := int(binary.LittleEndian.Uint32(serializedConsumers[offset:]))
    		offset += 4
    
    		consumers[transferHash] = make([]AddressHash, addressHashCount)
    		for i := 0; i < addressHashCount; i++ {
    			addressHash := AddressHash{}
    			if err := addressHash.UnmarshalBinary(serializedConsumers[offset:]); err != nil {
    				return nil, err
    			}
    			offset += addressHashLength
    
    			consumers[transferHash][i] = addressHash
    		}
    	}
    
    	return consumers, nil
    }
    
    // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////