Skip to content
Snippets Groups Projects
Select Git revision
  • 6a18a2f6d1e226c0c9236ba65c560d5ae6a029ac
  • release default protected
  • 11-22-implement-kv-interface-defined-in-collectiveversionedkvgo
  • hotfix/TestHostPool_UpdateNdf_AddFilter
  • XX-4719/announcementChannels
  • xx-4717/logLevel
  • jonah/noob-channel
  • master protected
  • XX-4707/tagDiskJson
  • xx-4698/notification-retry
  • hotfix/notifylockup
  • syncNodes
  • hotfix/localCB
  • XX-4677/NewChanManagerMobile
  • XX-4689/DmSync
  • duplicatePrefix
  • XX-4601/HavenInvites
  • finalizedUICallbacks
  • XX-4673/AdminKeySync
  • debugNotifID
  • anne/test
  • v4.7.5
  • v4.7.4
  • v4.7.3
  • v4.7.2
  • v4.7.1
  • v4.6.3
  • v4.6.1
  • v4.5.0
  • v4.4.4
  • v4.3.11
  • v4.3.8
  • v4.3.7
  • v4.3.6
  • v4.3.5
  • v4.2.0
  • v4.3.0
  • v4.3.4
  • v4.3.3
  • v4.3.2
  • v4.3.1
41 results

services_test.go

Blame
  • sentFileTransfers.go 7.80 KiB
    ////////////////////////////////////////////////////////////////////////////////
    // Copyright © 2020 xx network SEZC                                           //
    //                                                                            //
    // Use of this source code is governed by a license that can be found in the  //
    // LICENSE file                                                               //
    ////////////////////////////////////////////////////////////////////////////////
    
    package fileTransfer
    
    import (
    	"bytes"
    	"github.com/pkg/errors"
    	"gitlab.com/elixxir/client/interfaces"
    	"gitlab.com/elixxir/client/storage/versioned"
    	ftCrypto "gitlab.com/elixxir/crypto/fileTransfer"
    	"gitlab.com/xx_network/crypto/csprng"
    	"gitlab.com/xx_network/primitives/id"
    	"gitlab.com/xx_network/primitives/netTime"
    	"sync"
    	"time"
    )
    
    // Storage keys and versions.
    const (
    	sentFileTransfersPrefix  = "SentFileTransfersStore"
    	sentFileTransfersKey     = "SentFileTransfers"
    	sentFileTransfersVersion = 0
    )
    
    // Error messages.
    const (
    	saveSentTransfersListErr = "failed to save list of sent items in transfer map to storage: %+v"
    	loadSentTransfersListErr = "failed to load list of sent items in transfer map from storage: %+v"
    	loadSentTransfersErr     = "failed to load sent transfers from storage: %+v"
    
    	newSentTransferErr    = "failed to create new sent transfer: %+v"
    	getSentTransferErr    = "sent file transfer not found"
    	deleteSentTransferErr = "failed to delete sent transfer with ID %s from store: %+v"
    )
    
    // SentFileTransfers contains information for tracking sent file transfers.
    type SentFileTransfers struct {
    	transfers map[ftCrypto.TransferID]*SentTransfer
    	mux       sync.Mutex
    	kv        *versioned.KV
    }
    
    // NewSentFileTransfers creates a new SentFileTransfers with an empty map.
    func NewSentFileTransfers(kv *versioned.KV) (*SentFileTransfers, error) {
    	sft := &SentFileTransfers{
    		transfers: make(map[ftCrypto.TransferID]*SentTransfer),
    		kv:        kv.Prefix(sentFileTransfersPrefix),
    	}
    
    	return sft, sft.saveTransfersList()
    }
    
    // AddTransfer creates a new empty SentTransfer and adds it to the transfers
    // map.
    func (sft *SentFileTransfers) AddTransfer(recipient *id.ID,
    	key ftCrypto.TransferKey, parts [][]byte, numFps uint16,
    	progressCB interfaces.SentProgressCallback, period time.Duration,
    	rng csprng.Source) (ftCrypto.TransferID, error) {
    
    	sft.mux.Lock()
    	defer sft.mux.Unlock()
    
    	// Generate new transfer ID
    	tid, err := ftCrypto.NewTransferID(rng)
    	if err != nil {
    		return tid, errors.Errorf(addTransferNewIdErr, err)
    	}
    
    	// Generate a new SentTransfer and add it to the map
    	sft.transfers[tid], err = NewSentTransfer(
    		recipient, tid, key, parts, numFps, progressCB, period, sft.kv)
    	if err != nil {
    		return tid, errors.Errorf(newSentTransferErr, err)
    	}
    
    	// Update list of transfers in storage
    	err = sft.saveTransfersList()
    	if err != nil {
    		return tid, errors.Errorf(saveSentTransfersListErr, err)
    	}
    
    	return tid, nil
    }
    
    // GetTransfer returns the SentTransfer with the given transfer ID. An error is
    // returned if no corresponding transfer is found.
    func (sft *SentFileTransfers) GetTransfer(tid ftCrypto.TransferID) (
    	*SentTransfer, error) {
    	sft.mux.Lock()
    	defer sft.mux.Unlock()
    
    	rt, exists := sft.transfers[tid]
    	if !exists {
    		return nil, errors.New(getSentTransferErr)
    	}
    
    	return rt, nil
    }
    
    // DeleteTransfer removes the SentTransfer with the associated transfer ID
    // from memory and storage.
    func (sft *SentFileTransfers) DeleteTransfer(tid ftCrypto.TransferID) error {
    	sft.mux.Lock()
    	defer sft.mux.Unlock()
    
    	// Return an error if the transfer does not exist
    	_, exists := sft.transfers[tid]
    	if !exists {
    		return errors.New(getSentTransferErr)
    	}
    
    	// Delete all data the transfer saved to storage
    	err := sft.transfers[tid].delete()
    	if err != nil {
    		return errors.Errorf(deleteSentTransferErr, tid, err)
    	}
    
    	// Delete the transfer from memory
    	delete(sft.transfers, tid)
    
    	// Update the transfers list for the removed transfer
    	err = sft.saveTransfersList()
    	if err != nil {
    		return errors.Errorf(saveSentTransfersListErr, err)
    	}
    
    	return nil
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // Storage Functions                                                          //
    ////////////////////////////////////////////////////////////////////////////////
    
    // LoadSentFileTransfers loads all SentFileTransfers from storage.
    func LoadSentFileTransfers(kv *versioned.KV) (*SentFileTransfers, error) {
    	sft := &SentFileTransfers{
    		transfers: make(map[ftCrypto.TransferID]*SentTransfer),
    		kv:        kv.Prefix(sentFileTransfersPrefix),
    	}
    
    	// Get the list of transfer IDs corresponding to each sent transfer from
    	// storage
    	transfersList, err := sft.loadTransfersList()
    	if err != nil {
    		return nil, errors.Errorf(loadSentTransfersListErr, err)
    	}
    
    	// Load each transfer in the list from storage into the map
    	err = sft.loadTransfers(transfersList)
    	if err != nil {
    		return nil, errors.Errorf(loadSentTransfersErr, err)
    	}
    
    	return sft, nil
    }
    
    // NewOrLoadSentFileTransfers loads all SentFileTransfers from storage, if they
    // exist. Otherwise, a new SentFileTransfers is returned.
    func NewOrLoadSentFileTransfers(kv *versioned.KV) (*SentFileTransfers, error) {
    	sft := &SentFileTransfers{
    		transfers: make(map[ftCrypto.TransferID]*SentTransfer),
    		kv:        kv.Prefix(sentFileTransfersPrefix),
    	}
    
    	// If the transfer list cannot be loaded from storage, then create a new
    	// SentFileTransfers
    	vo, err := sft.kv.Get(sentFileTransfersKey, sentFileTransfersVersion)
    	if err != nil {
    		return NewSentFileTransfers(kv)
    	}
    
    	// Unmarshal data into list of saved transfer IDs
    	transfersList := unmarshalTransfersList(vo.Data)
    
    	// Load each transfer in the list from storage into the map
    	err = sft.loadTransfers(transfersList)
    	if err != nil {
    		return nil, errors.Errorf(loadSentTransfersErr, err)
    	}
    
    	return sft, nil
    }
    
    // saveTransfersList saves a list of items in the transfers map to storage.
    func (sft *SentFileTransfers) saveTransfersList() error {
    	// Create new versioned object with a list of items in the transfers map
    	obj := &versioned.Object{
    		Version:   sentFileTransfersVersion,
    		Timestamp: netTime.Now(),
    		Data:      sft.marshalTransfersList(),
    	}
    
    	// Save list of items in the transfers map to storage
    	return sft.kv.Set(sentFileTransfersKey, sentFileTransfersVersion, obj)
    }
    
    // loadTransfersList gets the list of transfer IDs corresponding to each saved
    // sent transfer from storage.
    func (sft *SentFileTransfers) loadTransfersList() ([]ftCrypto.TransferID, error) {
    	// Get transfers list from storage
    	vo, err := sft.kv.Get(sentFileTransfersKey, sentFileTransfersVersion)
    	if err != nil {
    		return nil, err
    	}
    
    	// Unmarshal data into list of saved transfer IDs
    	return unmarshalTransfersList(vo.Data), nil
    }
    
    // loadTransfers loads each SentTransfer from the list and adds them to the map.
    func (sft *SentFileTransfers) loadTransfers(list []ftCrypto.TransferID) error {
    	var err error
    
    	// Load each sentTransfer from storage into the map
    	for _, tid := range list {
    		sft.transfers[tid], err = loadSentTransfer(tid, sft.kv)
    		if err != nil {
    			return err
    		}
    	}
    
    	return nil
    }
    
    // marshalTransfersList creates a list of all transfer IDs in the transfers map
    // and serialises it.
    func (sft *SentFileTransfers) marshalTransfersList() []byte {
    	buff := bytes.NewBuffer(nil)
    	buff.Grow(ftCrypto.TransferIdLength * len(sft.transfers))
    
    	for tid := range sft.transfers {
    		buff.Write(tid.Bytes())
    	}
    
    	return buff.Bytes()
    }
    
    // unmarshalTransfersList deserializes a byte slice into a list of transfer IDs.
    func unmarshalTransfersList(b []byte) []ftCrypto.TransferID {
    	buff := bytes.NewBuffer(b)
    	list := make([]ftCrypto.TransferID, 0, buff.Len()/ftCrypto.TransferIdLength)
    
    	const size = ftCrypto.TransferIdLength
    	for n := buff.Next(size); len(n) == size; n = buff.Next(size) {
    		list = append(list, ftCrypto.UnmarshalTransferID(n))
    	}
    
    	return list
    }