github.com/0chain/gosdk@v1.17.11/zboxcore/sdk/downloadworker.go (about)

     1  package sdk
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/md5"
     7  	"encoding/hex"
     8  	"encoding/json"
     9  	"fmt"
    10  	"hash"
    11  	"io"
    12  	"io/ioutil"
    13  	"net/http"
    14  	"os"
    15  	"path/filepath"
    16  	"sort"
    17  	"strings"
    18  	"sync"
    19  	"sync/atomic"
    20  	"time"
    21  
    22  	"github.com/0chain/errors"
    23  	"github.com/0chain/gosdk/core/common"
    24  	"github.com/0chain/gosdk/core/sys"
    25  	"github.com/0chain/gosdk/zboxcore/blockchain"
    26  	"github.com/0chain/gosdk/zboxcore/client"
    27  	"github.com/0chain/gosdk/zboxcore/encryption"
    28  	"github.com/0chain/gosdk/zboxcore/fileref"
    29  	"github.com/0chain/gosdk/zboxcore/logger"
    30  	l "github.com/0chain/gosdk/zboxcore/logger"
    31  	"github.com/0chain/gosdk/zboxcore/marker"
    32  	"github.com/0chain/gosdk/zboxcore/zboxutil"
    33  	"github.com/klauspost/reedsolomon"
    34  	"go.dedis.ch/kyber/v3/group/edwards25519"
    35  	"golang.org/x/sync/errgroup"
    36  )
    37  
    38  const (
    39  	DOWNLOAD_CONTENT_FULL  = "full"
    40  	DOWNLOAD_CONTENT_THUMB = "thumbnail"
    41  )
    42  
    43  var (
    44  	extraCount = 2
    45  )
    46  
    47  type DownloadRequestOption func(dr *DownloadRequest)
    48  
    49  // WithDownloadProgressStorer set download progress storer of download request options.
    50  //   - storer: download progress storer instance, used to store download progress.
    51  func WithDownloadProgressStorer(storer DownloadProgressStorer) DownloadRequestOption {
    52  	return func(dr *DownloadRequest) {
    53  		dr.downloadStorer = storer
    54  	}
    55  }
    56  
    57  func WithWorkDir(workdir string) DownloadRequestOption {
    58  	return func(dr *DownloadRequest) {
    59  		dr.workdir = workdir
    60  	}
    61  }
    62  
    63  func WithFileCallback(cb func()) DownloadRequestOption {
    64  	return func(dr *DownloadRequest) {
    65  		dr.fileCallback = cb
    66  	}
    67  }
    68  
    69  type DownloadRequest struct {
    70  	allocationID       string
    71  	allocationTx       string
    72  	sig                string
    73  	allocOwnerID       string
    74  	allocOwnerPubKey   string
    75  	blobbers           []*blockchain.StorageNode
    76  	datashards         int
    77  	parityshards       int
    78  	remotefilepath     string
    79  	remotefilepathhash string
    80  	fileHandler        sys.File
    81  	localFilePath      string
    82  	startBlock         int64
    83  	endBlock           int64
    84  	chunkSize          int
    85  	numBlocks          int64
    86  	validationRootMap  map[string]*blobberFile
    87  	statusCallback     StatusCallback
    88  	ctx                context.Context
    89  	ctxCncl            context.CancelFunc
    90  	authTicket         *marker.AuthTicket
    91  	downloadMask       zboxutil.Uint128
    92  	encryptedKey       string
    93  	isDownloadCanceled bool
    94  	completedCallback  func(remotepath string, remotepathhash string)
    95  	fileCallback       func()
    96  	contentMode        string
    97  	Consensus
    98  	effectiveBlockSize int // blocksize - encryptionOverHead
    99  	ecEncoder          reedsolomon.Encoder
   100  	maskMu             *sync.Mutex
   101  	encScheme          encryption.EncryptionScheme
   102  	shouldVerify       bool
   103  	blocksPerShard     int64
   104  	connectionID       string
   105  	skip               bool
   106  	freeRead           bool
   107  	fRef               *fileref.FileRef
   108  	chunksPerShard     int64
   109  	size               int64
   110  	offset             int64
   111  	bufferMap          map[int]zboxutil.DownloadBuffer
   112  	downloadStorer     DownloadProgressStorer
   113  	workdir            string
   114  	downloadQueue      downloadQueue // Always initialize this queue with max time taken
   115  	isResume           bool
   116  	isEnterprise       bool
   117  }
   118  
   119  type downloadPriority struct {
   120  	timeTaken  int64
   121  	blobberIdx int
   122  }
   123  
   124  type downloadQueue []downloadPriority
   125  
   126  func (pq downloadQueue) Len() int { return len(pq) }
   127  
   128  func (pq downloadQueue) Less(i, j int) bool {
   129  	return pq[i].timeTaken < pq[j].timeTaken
   130  }
   131  
   132  type DownloadProgress struct {
   133  	ID               string `json:"id"`
   134  	LastWrittenBlock int    `json:"last_block"`
   135  	numBlocks        int    `json:"-"`
   136  }
   137  type blockData struct {
   138  	blockNum int
   139  	data     [][][]byte
   140  }
   141  
   142  func (req *DownloadRequest) removeFromMask(pos uint64) {
   143  	req.maskMu.Lock()
   144  	req.downloadMask = req.downloadMask.And(zboxutil.NewUint128(1).Lsh(pos).Not())
   145  	req.maskMu.Unlock()
   146  }
   147  
   148  func (req *DownloadRequest) getBlocksDataFromBlobbers(startBlock, totalBlock int64, timeRequest bool) ([][][]byte, error) {
   149  	shards := make([][][]byte, totalBlock)
   150  	for i := range shards {
   151  		shards[i] = make([][]byte, len(req.blobbers))
   152  	}
   153  
   154  	mask := req.downloadMask
   155  	requiredDownloads := req.consensusThresh
   156  	var (
   157  		remainingMask  zboxutil.Uint128
   158  		failed         int
   159  		err            error
   160  		downloadErrors []string
   161  	)
   162  
   163  	curReqDownloads := requiredDownloads
   164  	for {
   165  		remainingMask, failed, downloadErrors, err = req.downloadBlock(
   166  			startBlock, totalBlock, mask, curReqDownloads, shards, timeRequest)
   167  		if err != nil {
   168  			return nil, err
   169  		}
   170  		if failed == 0 || (timeRequest && mask.CountOnes()-failed >= requiredDownloads) {
   171  			break
   172  		}
   173  
   174  		if failed > remainingMask.CountOnes() {
   175  			return nil, errors.New("download_failed",
   176  				fmt.Sprintf("%d failed blobbers exceeded %d remaining blobbers."+
   177  					" Download errors: %s",
   178  					failed, remainingMask.CountOnes(), strings.Join(downloadErrors, " ")))
   179  		}
   180  
   181  		curReqDownloads = failed
   182  		mask = remainingMask
   183  	}
   184  	return shards, err
   185  }
   186  
   187  // getBlocksData will get data blocks for some interval from minimal blobers and aggregate them and
   188  // return to the caller
   189  func (req *DownloadRequest) getBlocksData(startBlock, totalBlock int64, timeRequest bool) ([][][]byte, error) {
   190  
   191  	shards, err := req.getBlocksDataFromBlobbers(startBlock, totalBlock, timeRequest)
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  
   196  	// erasure decoding
   197  	// Can we benefit from goroutine for erasure decoding??
   198  	// c := req.datashards * req.effectiveBlockSize
   199  	// data := make([]byte, req.datashards*req.effectiveBlockSize*int(totalBlock))
   200  	for i := range shards {
   201  		err = req.decodeEC(shards[i])
   202  		if err != nil {
   203  			return nil, err
   204  		}
   205  
   206  	}
   207  	return shards, nil
   208  }
   209  
   210  // downloadBlock This function will add download requests to the download channel which picks up
   211  // download requests and processes it.
   212  // This function will fill up `shards` in respective position and also return failed number of
   213  // blobbers along with remainingMask that are the blobbers that are not yet requested.
   214  func (req *DownloadRequest) downloadBlock(
   215  	startBlock, totalBlock int64,
   216  	mask zboxutil.Uint128, requiredDownloads int,
   217  	shards [][][]byte, timeRequest bool) (zboxutil.Uint128, int, []string, error) {
   218  
   219  	var remainingMask zboxutil.Uint128
   220  	activeBlobbers := mask.CountOnes()
   221  	if activeBlobbers < requiredDownloads {
   222  		return zboxutil.NewUint128(0), 0, nil, errors.New("insufficient_blobbers",
   223  			fmt.Sprintf("Required downloads %d, remaining active blobber %d",
   224  				req.consensusThresh, activeBlobbers))
   225  	}
   226  	if timeRequest {
   227  		requiredDownloads = activeBlobbers
   228  	}
   229  	rspCh := make(chan *downloadBlock, requiredDownloads)
   230  
   231  	var (
   232  		pos          uint64
   233  		c            int
   234  		skipDownload bool
   235  	)
   236  
   237  	for i := mask; !i.Equals64(0); i = i.And(zboxutil.NewUint128(1).Lsh(pos).Not()) {
   238  		if c == requiredDownloads {
   239  			remainingMask = i
   240  			break
   241  		}
   242  
   243  		pos = uint64(i.TrailingZeros())
   244  		blobberIdx := req.downloadQueue[pos].blobberIdx
   245  		blockDownloadReq := &BlockDownloadRequest{
   246  			allocationID:       req.allocationID,
   247  			allocationTx:       req.allocationTx,
   248  			allocOwnerID:       req.allocOwnerID,
   249  			authTicket:         req.authTicket,
   250  			blobber:            req.blobbers[blobberIdx],
   251  			blobberIdx:         blobberIdx,
   252  			maskIdx:            int(pos),
   253  			chunkSize:          req.chunkSize,
   254  			blockNum:           startBlock,
   255  			contentMode:        req.contentMode,
   256  			result:             rspCh,
   257  			ctx:                req.ctx,
   258  			remotefilepath:     req.remotefilepath,
   259  			remotefilepathhash: req.remotefilepathhash,
   260  			numBlocks:          totalBlock,
   261  			encryptedKey:       req.encryptedKey,
   262  			shouldVerify:       req.shouldVerify,
   263  			connectionID:       req.connectionID,
   264  		}
   265  
   266  		if blockDownloadReq.blobber.IsSkip() {
   267  			rspCh <- &downloadBlock{
   268  				Success: false,
   269  				idx:     blockDownloadReq.blobberIdx,
   270  				err:     errors.New("", "skip blobber by previous errors")}
   271  			skipDownload = true
   272  		}
   273  
   274  		if !skipDownload {
   275  			bf := req.validationRootMap[blockDownloadReq.blobber.ID]
   276  			blockDownloadReq.blobberFile = bf
   277  			if req.shouldVerify {
   278  				go AddBlockDownloadReq(req.ctx, blockDownloadReq, nil, req.effectiveBlockSize)
   279  			} else {
   280  				go AddBlockDownloadReq(req.ctx, blockDownloadReq, req.bufferMap[blobberIdx], req.effectiveBlockSize)
   281  			}
   282  		}
   283  
   284  		c++
   285  	}
   286  
   287  	var failed int32
   288  	downloadErrors := make([]string, requiredDownloads)
   289  	wg := &sync.WaitGroup{}
   290  	for i := 0; i < requiredDownloads; i++ {
   291  		result := <-rspCh
   292  		wg.Add(1)
   293  		go func(i int) {
   294  			var err error
   295  			defer func() {
   296  				if err != nil {
   297  					totalFail := atomic.AddInt32(&failed, 1)
   298  					// if first request remove from end as we will convert the slice into heap
   299  					if timeRequest {
   300  						req.removeFromMask(uint64(activeBlobbers - int(totalFail)))
   301  					} else {
   302  						req.removeFromMask(uint64(result.maskIdx))
   303  					}
   304  					downloadErrors[i] = fmt.Sprintf("Error %s from %s",
   305  						err.Error(), req.blobbers[result.idx].Baseurl)
   306  					logger.Logger.Error(err)
   307  					if req.bufferMap != nil && req.bufferMap[result.idx] != nil {
   308  						req.bufferMap[result.idx].ReleaseChunk(int(req.startBlock))
   309  					}
   310  				} else if timeRequest {
   311  					req.downloadQueue[result.maskIdx].timeTaken = result.timeTaken
   312  				}
   313  				wg.Done()
   314  			}()
   315  			if !result.Success {
   316  				err = fmt.Errorf("Unsuccessful download. Error: %v", result.err)
   317  				return
   318  			}
   319  			err = req.fillShards(shards, result)
   320  		}(i)
   321  	}
   322  
   323  	wg.Wait()
   324  	return remainingMask, int(failed), downloadErrors, nil
   325  }
   326  
   327  // decodeEC will reconstruct shards and verify it
   328  func (req *DownloadRequest) decodeEC(shards [][]byte) (err error) {
   329  	err = req.ecEncoder.ReconstructData(shards)
   330  	if err != nil {
   331  		return
   332  	}
   333  	// c := len(shards[0])
   334  	// data = make([]byte, req.datashards*c)
   335  	// for i := 0; i < req.datashards; i++ {
   336  	// 	index := i * c
   337  	// 	copy(data[index:index+c], shards[i])
   338  	// }
   339  	return nil
   340  }
   341  
   342  //shards -> shards[i][data]
   343  
   344  // fillShards will fill `shards` with data from blobbers that belongs to specific
   345  // blockNumber and blobber's position index in an allocation
   346  func (req *DownloadRequest) fillShards(shards [][][]byte, result *downloadBlock) (err error) {
   347  
   348  	for i := 0; i < len(result.BlockChunks); i++ {
   349  		var data []byte
   350  		if req.encryptedKey != "" {
   351  			data, err = req.getDecryptedData(result, i)
   352  			if err != nil {
   353  				return err
   354  			}
   355  		} else {
   356  			data = result.BlockChunks[i]
   357  		}
   358  		if i >= len(shards) || len(shards[i]) <= result.idx {
   359  			l.Logger.Error("Invalid shard index", result.idx, len(shards))
   360  			return errors.New("invalid_shard_index", fmt.Sprintf("Invalid shard index %d shard len: %d shard block len: %d", result.idx, len(shards), i))
   361  		}
   362  		shards[i][result.idx] = data
   363  	}
   364  	return
   365  }
   366  
   367  // getDecryptedData will decrypt encrypted data and return it.
   368  func (req *DownloadRequest) getDecryptedData(result *downloadBlock, blockNum int) (data []byte, err error) {
   369  	if req.authTicket != nil {
   370  		return req.getDecryptedDataForAuthTicket(result, blockNum)
   371  	}
   372  
   373  	headerBytes := result.BlockChunks[blockNum][:EncryptionHeaderSize]
   374  	headerBytes = bytes.Trim(headerBytes, "\x00")
   375  
   376  	if len(headerBytes) != EncryptionHeaderSize {
   377  		logger.Logger.Error("Block has invalid header", req.blobbers[result.idx].Baseurl)
   378  		return nil, errors.New(
   379  			"invalid_header",
   380  			fmt.Sprintf("Block from %s has invalid header. Required header size: %d, got %d",
   381  				req.blobbers[result.idx].Baseurl, EncryptionHeaderSize, len(headerBytes)))
   382  	}
   383  
   384  	encMsg := &encryption.EncryptedMessage{}
   385  	encMsg.EncryptedData = result.BlockChunks[blockNum][EncryptionHeaderSize:]
   386  	encMsg.MessageChecksum, encMsg.OverallChecksum = string(headerBytes[:128]), string(headerBytes[128:])
   387  	encMsg.EncryptedKey = req.encScheme.GetEncryptedKey()
   388  	decryptedBytes, err := req.encScheme.Decrypt(encMsg)
   389  	if err != nil {
   390  		logger.Logger.Error("Block decryption failed", req.blobbers[result.idx].Baseurl, err)
   391  		return nil, errors.New(
   392  			"decryption_error",
   393  			fmt.Sprintf("Decryption error %s while decrypting data from %s blobber",
   394  				err.Error(), req.blobbers[result.idx].Baseurl))
   395  	}
   396  	return decryptedBytes, nil
   397  }
   398  
   399  // getDecryptedDataForAuthTicket will return decrypt shared encrypted data using re-encryption/re-decryption
   400  // mechanism
   401  func (req *DownloadRequest) getDecryptedDataForAuthTicket(result *downloadBlock, blockNum int) (data []byte, err error) {
   402  	suite := edwards25519.NewBlakeSHA256Ed25519()
   403  	reEncMessage := &encryption.ReEncryptedMessage{
   404  		D1: suite.Point(),
   405  		D4: suite.Point(),
   406  		D5: suite.Point(),
   407  	}
   408  	err = reEncMessage.Unmarshal(result.BlockChunks[blockNum])
   409  	if err != nil {
   410  		logger.Logger.Error("ReEncrypted Block unmarshall failed", req.blobbers[result.idx].Baseurl, err)
   411  		return nil, err
   412  	}
   413  	decrypted, err := req.encScheme.ReDecrypt(reEncMessage)
   414  	if err != nil {
   415  		logger.Logger.Error("Block redecryption failed", req.blobbers[result.idx].Baseurl, err)
   416  		return nil, err
   417  	}
   418  	return decrypted, nil
   419  }
   420  
   421  // processDownload will setup download parameters and downloads data with given
   422  // start block, end block and number of blocks to download in single request.
   423  // This will also write data to the file handler and will verify content by calculating content hash.
   424  func (req *DownloadRequest) processDownload() {
   425  	ctx := req.ctx
   426  	if req.completedCallback != nil {
   427  		defer req.completedCallback(req.remotefilepath, req.remotefilepathhash)
   428  	}
   429  	if req.fileCallback != nil {
   430  		defer func() {
   431  			if !req.skip {
   432  				req.fileCallback()
   433  			}
   434  		}()
   435  	}
   436  	defer req.ctxCncl()
   437  	remotePathCB := req.remotefilepath
   438  	if remotePathCB == "" {
   439  		remotePathCB = req.remotefilepathhash
   440  	}
   441  
   442  	var op = OpDownload
   443  	if req.contentMode == DOWNLOAD_CONTENT_THUMB {
   444  		op = opThumbnailDownload
   445  	}
   446  	fRef := req.fRef
   447  	if fRef != nil && fRef.ActualFileHash == emptyFileDataHash {
   448  		logger.Logger.Info("File is empty")
   449  		_, err := req.fileHandler.Write([]byte(emptyFileDataHash))
   450  		if err != nil {
   451  			req.errorCB(errors.Wrap(err, "Write file failed"), remotePathCB)
   452  			return
   453  		}
   454  		req.fileHandler.Sync() //nolint
   455  		if req.statusCallback != nil && !req.skip {
   456  			req.statusCallback.Completed(
   457  				req.allocationID, remotePathCB, fRef.Name, fRef.MimeType, 32, op)
   458  		}
   459  		return
   460  	}
   461  	size, chunksPerShard, blocksPerShard := req.size, req.chunksPerShard, req.blocksPerShard
   462  
   463  	now := time.Now()
   464  	err := req.initEC()
   465  	if err != nil {
   466  		logger.Logger.Error(err)
   467  		req.errorCB(
   468  			fmt.Errorf("Error while initializing file ref. Error: %v",
   469  				err), remotePathCB)
   470  		return
   471  	}
   472  	elapsedInitEC := time.Since(now)
   473  	if req.encryptedKey != "" {
   474  		err = req.initEncryption()
   475  		if err != nil {
   476  			req.errorCB(
   477  				fmt.Errorf("Error while initializing encryption"), remotePathCB,
   478  			)
   479  			return
   480  		}
   481  	}
   482  	elapsedInitEncryption := time.Since(now) - elapsedInitEC
   483  
   484  	var downloaded int
   485  	startBlock, endBlock, numBlocks := req.startBlock, req.endBlock, req.numBlocks
   486  	// remainingSize should be calculated based on startBlock number
   487  	// otherwise end data will have null bytes.
   488  	remainingSize := size - startBlock*int64(req.effectiveBlockSize)*int64(req.datashards)
   489  
   490  	if endBlock*int64(req.effectiveBlockSize)*int64(req.datashards) < req.size {
   491  		remainingSize = blocksPerShard * int64(req.effectiveBlockSize) * int64(req.datashards)
   492  	} else if req.isResume {
   493  		remainingSize = size
   494  	}
   495  
   496  	if memFile, ok := req.fileHandler.(*sys.MemFile); ok {
   497  		memFile.InitBuffer(int(remainingSize))
   498  	}
   499  
   500  	if req.statusCallback != nil {
   501  		// Started will also initialize progress bar. So without calling this function
   502  		// other callback's call will panic
   503  		req.statusCallback.Started(req.allocationID, remotePathCB, op, int(remainingSize))
   504  	}
   505  
   506  	if req.shouldVerify {
   507  		if req.isEnterprise || (req.authTicket != nil && req.encryptedKey != "") {
   508  			req.shouldVerify = false
   509  		}
   510  	}
   511  	n := int((endBlock - startBlock + numBlocks - 1) / numBlocks)
   512  
   513  	// Buffered channel to hold the blocks as they are downloaded
   514  	blocks := make(chan blockData, n)
   515  
   516  	var (
   517  		actualFileHasher  hash.Hash
   518  		isPREAndWholeFile bool
   519  	)
   520  
   521  	if !req.shouldVerify && (startBlock == 0 && endBlock == chunksPerShard) && shouldVerifyHash {
   522  		actualFileHasher = md5.New()
   523  		isPREAndWholeFile = true
   524  	}
   525  
   526  	toSync := false
   527  	if _, ok := req.fileHandler.(*sys.MemChanFile); ok {
   528  		toSync = true
   529  	}
   530  	var writerAt bool
   531  	writeAtHandler, ok := req.fileHandler.(io.WriterAt)
   532  	if ok {
   533  		writerAt = true
   534  	}
   535  	bufBlocks := int(numBlocks)
   536  	if n == 1 && endBlock-startBlock < numBlocks {
   537  		bufBlocks = int(endBlock - startBlock)
   538  	}
   539  	if !req.shouldVerify {
   540  		var pos uint64
   541  		req.bufferMap = make(map[int]zboxutil.DownloadBuffer)
   542  		defer func() {
   543  			l.Logger.Debug("Clearing download buffers: ", len(req.bufferMap))
   544  			for ind, rb := range req.bufferMap {
   545  				rb.ClearBuffer()
   546  				delete(req.bufferMap, ind)
   547  			}
   548  			req.bufferMap = nil
   549  		}()
   550  		sz := downloadWorkerCount + extraCount
   551  		if sz > n {
   552  			sz = n
   553  		}
   554  
   555  		for i := req.downloadMask; !i.Equals64(0); i = i.And(zboxutil.NewUint128(1).Lsh(pos).Not()) {
   556  			pos = uint64(i.TrailingZeros())
   557  			blobberIdx := int(pos)
   558  			if writerAt {
   559  				req.bufferMap[blobberIdx] = zboxutil.NewDownloadBufferWithChan(sz, bufBlocks, req.effectiveBlockSize)
   560  			} else {
   561  				bufMask := zboxutil.NewDownloadBufferWithMask(sz, bufBlocks, req.effectiveBlockSize)
   562  				bufMask.SetNumBlocks(int(numBlocks))
   563  				req.bufferMap[blobberIdx] = bufMask
   564  			}
   565  		}
   566  	}
   567  	// reset mask to number of active blobbers, not it denotes index of download queue and not blobber index
   568  	activeBlobbers := req.downloadMask.CountOnes()
   569  	req.downloadMask = zboxutil.NewUint128(1).Lsh(uint64(activeBlobbers)).Sub64(1)
   570  
   571  	logger.Logger.Info(
   572  		fmt.Sprintf("Downloading file with size: %d from start block: %d and end block: %d. "+
   573  			"Blocks per blobber: %d remainingSize: %d and total requests: %d", size, req.startBlock, req.endBlock, blocksPerShard, remainingSize, n),
   574  	)
   575  
   576  	writeCtx, writeCancel := context.WithCancel(ctx)
   577  	defer writeCancel()
   578  	var wg sync.WaitGroup
   579  
   580  	if !writerAt {
   581  		wg.Add(1)
   582  		// Handle writing the blocks in order as soon as they are downloaded
   583  		go func() {
   584  			defer wg.Done()
   585  			buffer := make(map[int][][][]byte)
   586  			for i := 0; i < n; i++ {
   587  				select {
   588  				case <-writeCtx.Done():
   589  					goto breakLoop
   590  				default:
   591  				}
   592  				if data, ok := buffer[i]; ok {
   593  					// If the block we need to write next is already in the buffer, write it
   594  					hashWg := &sync.WaitGroup{}
   595  					if isPREAndWholeFile {
   596  						if i == n-1 {
   597  							writeData(actualFileHasher, data, req.datashards, int(remainingSize)) //nolint
   598  							if calculatedFileHash, ok := checkHash(actualFileHasher, fRef, req.contentMode); !ok {
   599  								req.errorCB(fmt.Errorf("Expected actual file hash %s, calculated file hash %s",
   600  									fRef.ActualFileHash, calculatedFileHash), remotePathCB)
   601  								return
   602  							}
   603  						} else {
   604  							hashWg.Add(1)
   605  							go func() {
   606  								writeData(actualFileHasher, data, req.datashards, int(remainingSize)) //nolint
   607  								hashWg.Done()
   608  							}()
   609  						}
   610  					}
   611  
   612  					totalWritten, err := writeData(req.fileHandler, data, req.datashards, int(remainingSize))
   613  					if err != nil {
   614  						req.errorCB(errors.Wrap(err, "Write file failed"), remotePathCB)
   615  						return
   616  					}
   617  					if toSync {
   618  						req.fileHandler.Sync() //nolint
   619  					}
   620  
   621  					if isPREAndWholeFile {
   622  						hashWg.Wait()
   623  					}
   624  					for _, rb := range req.bufferMap {
   625  						rb.ReleaseChunk(int(startBlock + int64(i)*numBlocks))
   626  					}
   627  					downloaded = downloaded + totalWritten
   628  					remainingSize -= int64(totalWritten)
   629  
   630  					if req.statusCallback != nil {
   631  						req.statusCallback.InProgress(req.allocationID, remotePathCB, op, downloaded, nil)
   632  					}
   633  
   634  					// Remove the block from the buffer
   635  					delete(buffer, i)
   636  				} else {
   637  					// If the block we need to write next is not in the buffer, wait for it
   638  					for block := range blocks {
   639  						if block.blockNum == i {
   640  							// Write the data
   641  							hashWg := &sync.WaitGroup{}
   642  							if isPREAndWholeFile {
   643  								if i == n-1 {
   644  									writeData(actualFileHasher, block.data, req.datashards, int(remainingSize)) //nolint
   645  									if calculatedFileHash, ok := checkHash(actualFileHasher, fRef, req.contentMode); !ok {
   646  										req.errorCB(fmt.Errorf("Expected actual file hash %s, calculated file hash %s",
   647  											fRef.ActualFileHash, calculatedFileHash), remotePathCB)
   648  										return
   649  									}
   650  								} else {
   651  									hashWg.Add(1)
   652  									go func() {
   653  										writeData(actualFileHasher, block.data, req.datashards, int(remainingSize)) //nolint
   654  										hashWg.Done()
   655  									}()
   656  								}
   657  							}
   658  
   659  							totalWritten, err := writeData(req.fileHandler, block.data, req.datashards, int(remainingSize))
   660  							if err != nil {
   661  								req.errorCB(errors.Wrap(err, "Write file failed"), remotePathCB)
   662  								return
   663  							}
   664  
   665  							if toSync {
   666  								req.fileHandler.Sync() //nolint
   667  							}
   668  
   669  							if isPREAndWholeFile {
   670  								hashWg.Wait()
   671  							}
   672  							for _, rb := range req.bufferMap {
   673  								rb.ReleaseChunk(int(startBlock + int64(i)*numBlocks))
   674  							}
   675  
   676  							downloaded = downloaded + totalWritten
   677  							remainingSize -= int64(totalWritten)
   678  
   679  							if req.statusCallback != nil {
   680  								req.statusCallback.InProgress(req.allocationID, remotePathCB, op, downloaded, nil)
   681  							}
   682  
   683  							break
   684  						} else {
   685  							// If this block is not the one we're waiting for, store it in the buffer
   686  							buffer[block.blockNum] = block.data
   687  						}
   688  					}
   689  				}
   690  			}
   691  		breakLoop:
   692  		}()
   693  	}
   694  	if req.downloadStorer != nil {
   695  		storerCtx, storerCancel := context.WithCancel(ctx)
   696  		defer storerCancel()
   697  		req.downloadStorer.Start(storerCtx)
   698  	}
   699  
   700  	var progressLock sync.Mutex
   701  	firstReqWG := sync.WaitGroup{}
   702  	firstReqWG.Add(1)
   703  	eg, egCtx := errgroup.WithContext(ctx)
   704  	eg.SetLimit(downloadWorkerCount + extraCount)
   705  	for i := 0; i < n; i++ {
   706  		j := i
   707  		if i == 1 {
   708  			firstReqWG.Wait()
   709  			sort.Slice(req.downloadQueue, req.downloadQueue.Less)
   710  		}
   711  		select {
   712  		case <-egCtx.Done():
   713  			goto breakDownloadLoop
   714  		default:
   715  		}
   716  		eg.Go(func() error {
   717  
   718  			if j == 0 {
   719  				defer firstReqWG.Done()
   720  			}
   721  			blocksToDownload := numBlocks
   722  			if startBlock+int64(j)*numBlocks+numBlocks > endBlock {
   723  				blocksToDownload = endBlock - (startBlock + int64(j)*numBlocks)
   724  			}
   725  			data, err := req.getBlocksData(startBlock+int64(j)*numBlocks, blocksToDownload, j == 0)
   726  			if req.isDownloadCanceled {
   727  				return errors.New("download_abort", "Download aborted by user")
   728  			}
   729  			if err != nil {
   730  				return errors.Wrap(err, fmt.Sprintf("Download failed for block %d. ", startBlock+int64(j)*numBlocks))
   731  			}
   732  			if !writerAt {
   733  				blocks <- blockData{blockNum: j, data: data}
   734  			} else {
   735  				var offset int64
   736  				if req.downloadStorer != nil {
   737  					offset = (startBlock + int64(j)*numBlocks) * int64(req.effectiveBlockSize) * int64(req.datashards)
   738  				} else {
   739  					offset = int64(j) * numBlocks * int64(req.effectiveBlockSize) * int64(req.datashards)
   740  				}
   741  				var total int
   742  				if j == n-1 {
   743  					total, err = writeAtData(writeAtHandler, data, req.datashards, offset, int(remainingSize-offset))
   744  				} else {
   745  					total, err = writeAtData(writeAtHandler, data, req.datashards, offset, -1)
   746  				}
   747  				if err != nil {
   748  					logger.Logger.Error("downloadFailed: ", startBlock+int64(j)*numBlocks, " remainingSize: ", remainingSize, " offset: ", offset)
   749  					return errors.Wrap(err, fmt.Sprintf("WriteAt failed for block %d. ", startBlock+int64(j)*numBlocks))
   750  				}
   751  				for _, rb := range req.bufferMap {
   752  					rb.ReleaseChunk(int(startBlock + int64(j)*numBlocks))
   753  				}
   754  				if req.downloadStorer != nil {
   755  					go req.downloadStorer.Update(int(startBlock + int64(j)*numBlocks + blocksToDownload))
   756  				}
   757  				if req.statusCallback != nil {
   758  					progressLock.Lock()
   759  					downloaded += total
   760  					req.statusCallback.InProgress(req.allocationID, remotePathCB, op, int(downloaded), nil)
   761  					progressLock.Unlock()
   762  				}
   763  			}
   764  			return nil
   765  		})
   766  	breakDownloadLoop:
   767  	}
   768  	if err := eg.Wait(); err != nil {
   769  		writeCancel()
   770  		close(blocks)
   771  		wg.Wait()
   772  		req.errorCB(err, remotePathCB)
   773  		return
   774  	}
   775  
   776  	close(blocks)
   777  	wg.Wait()
   778  	// req.fileHandler.Sync() //nolint
   779  	elapsedGetBlocksAndWrite := time.Since(now) - elapsedInitEC - elapsedInitEncryption
   780  	l.Logger.Debug(fmt.Sprintf("[processDownload] Timings:\n allocation_id: %s,\n remotefilepath: %s,\n initEC: %d ms,\n initEncryption: %d ms,\n getBlocks and writes: %d ms",
   781  		req.allocationID,
   782  		req.remotefilepath,
   783  		elapsedInitEC.Milliseconds(),
   784  		elapsedInitEncryption.Milliseconds(),
   785  		elapsedGetBlocksAndWrite.Milliseconds(),
   786  	))
   787  
   788  	if req.statusCallback != nil && !req.skip {
   789  		req.statusCallback.Completed(
   790  			req.allocationID, remotePathCB, fRef.Name, fRef.MimeType, int(size), op)
   791  	}
   792  	if req.downloadStorer != nil {
   793  		req.downloadStorer.Remove() //nolint:errcheck
   794  	}
   795  }
   796  
   797  func checkHash(actualFileHasher hash.Hash, fref *fileref.FileRef, contentMode string) (string, bool) {
   798  	calculatedFileHash := hex.EncodeToString(actualFileHasher.Sum(nil))
   799  	if contentMode == DOWNLOAD_CONTENT_THUMB {
   800  		return calculatedFileHash, calculatedFileHash == fref.ActualThumbnailHash
   801  	} else {
   802  		return calculatedFileHash, calculatedFileHash == fref.ActualFileHash
   803  	}
   804  }
   805  
   806  func (req *DownloadRequest) submitReadMarker(blobber *blockchain.StorageNode, readCount int64) (err error) {
   807  	var retryCount = 3
   808  	for retryCount > 0 {
   809  		if err = req.attemptSubmitReadMarker(blobber, readCount); err != nil {
   810  			logger.Logger.Error(fmt.Sprintf("Error while attempting to submit readmarker %v, retry: %d", err, retryCount))
   811  			if IsErrCode(err, NotEnoughTokens) || IsErrCode(err, InvalidAuthTicket) || IsErrCode(err, InvalidShare) {
   812  				return err
   813  			}
   814  			if IsErrCode(err, LockExists) || IsErrCode(err, RateLimitError) {
   815  				continue
   816  			}
   817  			retryCount--
   818  		} else {
   819  			return nil
   820  		}
   821  	}
   822  	blobber.SetSkip(true)
   823  	return fmt.Errorf("submit read marker failed after retries: %w", err)
   824  }
   825  
   826  func (req *DownloadRequest) attemptSubmitReadMarker(blobber *blockchain.StorageNode, readCount int64) error {
   827  	lockBlobberReadCtr(req.allocationID, blobber.ID)
   828  	defer unlockBlobberReadCtr(req.allocationID, blobber.ID)
   829  	rm := &marker.ReadMarker{
   830  		ClientID:        client.GetClientID(),
   831  		ClientPublicKey: client.GetClientPublicKey(),
   832  		BlobberID:       blobber.ID,
   833  		AllocationID:    req.allocationID,
   834  		OwnerID:         req.allocOwnerID,
   835  		Timestamp:       common.Now(),
   836  		ReadCounter:     getBlobberReadCtr(req.allocationID, blobber.ID) + readCount,
   837  		SessionRC:       readCount,
   838  	}
   839  	err := rm.Sign()
   840  	if err != nil {
   841  		return fmt.Errorf("error signing read marker: %w", err)
   842  	}
   843  	logger.Logger.Debug(fmt.Sprintf("Attempting to submit RM: ReadCounter: %d, SessionRC: %d, BlobberID: %v", rm.ReadCounter, rm.SessionRC, rm.BlobberID))
   844  	rmData, err := json.Marshal(rm)
   845  	if err != nil {
   846  		return fmt.Errorf("error marshaling read marker: %w", err)
   847  	}
   848  	httpreq, err := zboxutil.NewRedeemRequest(blobber.Baseurl, req.allocationID, req.allocationTx)
   849  	if err != nil {
   850  		return fmt.Errorf("error creating download request: %w", err)
   851  	}
   852  
   853  	header := &DownloadRequestHeader{
   854  		PathHash:     req.remotefilepathhash,
   855  		ReadMarker:   rmData,
   856  		ConnectionID: req.connectionID,
   857  	}
   858  	header.ToHeader(httpreq)
   859  
   860  	ctx, cancel := context.WithTimeout(req.ctx, 30*time.Second)
   861  	defer cancel()
   862  
   863  	err = zboxutil.HttpDo(ctx, cancel, httpreq, func(resp *http.Response, err error) error {
   864  		if err != nil {
   865  			return err
   866  		}
   867  		if resp.Body != nil {
   868  			defer resp.Body.Close()
   869  		}
   870  
   871  		if resp.StatusCode == http.StatusTooManyRequests {
   872  			logger.Logger.Info(blobber.Baseurl,
   873  				" got too many request error. Retrying")
   874  			var r int
   875  			r, err = zboxutil.GetRateLimitValue(resp)
   876  			if err != nil {
   877  				logger.Logger.Error(err)
   878  				return errors.New("rate_limit_error", "Error while getting rate limit value")
   879  			}
   880  			time.Sleep(time.Duration(r) * time.Second)
   881  			return errors.New("rate_limit_error", "Too many requests")
   882  		}
   883  
   884  		if resp.StatusCode != http.StatusOK {
   885  			return req.handleReadMarkerError(resp, blobber, rm)
   886  		}
   887  		incBlobberReadCtr(req.allocationID, blobber.ID, readCount)
   888  
   889  		logger.Logger.Debug("Submit readmarker 200 OK")
   890  
   891  		return nil
   892  	})
   893  	return err
   894  }
   895  
   896  func (req *DownloadRequest) handleReadMarkerError(resp *http.Response, blobber *blockchain.StorageNode, rm *marker.ReadMarker) error {
   897  	respBody, err := ioutil.ReadAll(resp.Body)
   898  	if err != nil {
   899  		return err
   900  	}
   901  
   902  	appErrorCode := resp.Header.Get("X-App-Error-Code")
   903  	if appErrorCode != "" {
   904  		if appErrorCode == NotEnoughTokens {
   905  			logger.Logger.Debug(fmt.Sprintf("NotEnoughTokens - blobberID: %v", blobber.ID))
   906  			blobber.SetSkip(true)
   907  			return errors.New(NotEnoughTokens, string(respBody))
   908  		}
   909  		if appErrorCode == InvalidAuthTicket {
   910  			logger.Logger.Debug(fmt.Sprintf("InvalidAuthTicket - blobberID: %v", blobber.ID))
   911  			blobber.SetSkip(true)
   912  			return errors.New(InvalidAuthTicket, string(respBody))
   913  		}
   914  		if appErrorCode == InvalidShare {
   915  			logger.Logger.Debug(fmt.Sprintf("InvalidShare - blobberID: %v", blobber.ID))
   916  			blobber.SetSkip(true)
   917  			return errors.New(InvalidShare, string(respBody))
   918  		}
   919  		if appErrorCode == LockExists {
   920  			logger.Logger.Debug(fmt.Sprintf("LockExists - blobberID: %v", blobber.ID))
   921  			time.Sleep(time.Second * 1)
   922  			return errors.New(LockExists, string(respBody))
   923  		}
   924  	}
   925  
   926  	var rspData downloadBlock
   927  	if err = json.Unmarshal(respBody, &rspData); err == nil && rspData.LatestRM != nil {
   928  		if err := rm.ValidateWithOtherRM(rspData.LatestRM); err != nil {
   929  			return err
   930  		}
   931  
   932  		lastBlobberReadCounter := getBlobberReadCtr(req.allocationID, blobber.ID)
   933  		if rspData.LatestRM.ReadCounter != lastBlobberReadCounter {
   934  			setBlobberReadCtr(req.allocationID, blobber.ID, rspData.LatestRM.ReadCounter)
   935  			return fmt.Errorf("stale_read_marker: readmarker counter is not in sync with latest counter. Last blobber read counter: %d, but readmarker's counter was: %d", rspData.LatestRM.ReadCounter, lastBlobberReadCounter)
   936  		}
   937  		return fmt.Errorf("download_error: response status: %d, error: %v", resp.StatusCode, rspData.err)
   938  	}
   939  
   940  	return fmt.Errorf("response_error: %s", string(respBody))
   941  }
   942  
   943  func IsErrCode(err error, code string) bool {
   944  	if err == nil {
   945  		return false
   946  	}
   947  	if e, ok := err.(*errors.Error); ok && e.Code == code {
   948  		return true
   949  	}
   950  	return strings.Contains(err.Error(), code)
   951  }
   952  
   953  // initEC will initialize erasure encoder/decoder
   954  func (req *DownloadRequest) initEC() error {
   955  	var err error
   956  	req.ecEncoder, err = reedsolomon.New(
   957  		req.datashards, req.parityshards,
   958  		reedsolomon.WithAutoGoroutines(int(req.effectiveBlockSize)))
   959  
   960  	if err != nil {
   961  		return errors.New("init_ec",
   962  			fmt.Sprintf("Got error %s, while initializing erasure encoder", err.Error()))
   963  	}
   964  	return nil
   965  }
   966  
   967  // initEncryption will initialize encScheme with client's keys
   968  func (req *DownloadRequest) initEncryption() (err error) {
   969  	req.encScheme = encryption.NewEncryptionScheme()
   970  	mnemonic := client.GetClient().Mnemonic
   971  	if mnemonic != "" {
   972  		_, err = req.encScheme.Initialize(client.GetClient().Mnemonic)
   973  		if err != nil {
   974  			return err
   975  		}
   976  	} else {
   977  		return errors.New("invalid_mnemonic", "Invalid mnemonic")
   978  	}
   979  
   980  	err = req.encScheme.InitForDecryption("filetype:audio", req.encryptedKey)
   981  	if err != nil {
   982  		return err
   983  	}
   984  	return nil
   985  }
   986  
   987  func (req *DownloadRequest) errorCB(err error, remotePathCB string) {
   988  	var op = OpDownload
   989  	if req.contentMode == DOWNLOAD_CONTENT_THUMB {
   990  		op = opThumbnailDownload
   991  	}
   992  	if req.downloadStorer != nil && !strings.Contains(err.Error(), "context canceled") {
   993  		req.downloadStorer.Remove() //nolint: errcheck
   994  	}
   995  	if req.skip {
   996  		return
   997  	}
   998  	req.skip = true
   999  	if req.localFilePath != "" {
  1000  		if info, err := req.fileHandler.Stat(); err == nil && info.Size() == 0 {
  1001  			os.Remove(req.localFilePath) //nolint: errcheck
  1002  		}
  1003  	}
  1004  	if req.fileHandler != nil {
  1005  		req.fileHandler.Close() //nolint: errcheck
  1006  	}
  1007  	if req.statusCallback != nil {
  1008  		req.statusCallback.Error(
  1009  			req.allocationID, remotePathCB, op, err)
  1010  	}
  1011  }
  1012  
  1013  func (req *DownloadRequest) calculateShardsParams(
  1014  	fRef *fileref.FileRef) (chunksPerShard int64, err error) {
  1015  
  1016  	size := fRef.ActualFileSize
  1017  	if req.contentMode == DOWNLOAD_CONTENT_THUMB {
  1018  		if fRef.ActualThumbnailSize == 0 {
  1019  			return 0, errors.New("invalid_request", "Thumbnail does not exist")
  1020  		}
  1021  		size = fRef.ActualThumbnailSize
  1022  	}
  1023  	req.size = size
  1024  	req.encryptedKey = fRef.EncryptedKey
  1025  	req.chunkSize = int(fRef.ChunkSize)
  1026  
  1027  	effectivePerShardSize := (size + int64(req.datashards) - 1) / int64(req.datashards)
  1028  	effectiveBlockSize := fRef.ChunkSize
  1029  	if fRef.EncryptedKey != "" {
  1030  		effectiveBlockSize -= EncryptionHeaderSize + EncryptedDataPaddingSize
  1031  	}
  1032  
  1033  	req.effectiveBlockSize = int(effectiveBlockSize)
  1034  
  1035  	chunksPerShard = (effectivePerShardSize + effectiveBlockSize - 1) / effectiveBlockSize
  1036  
  1037  	info, err := req.fileHandler.Stat()
  1038  	if err != nil {
  1039  		return 0, err
  1040  	}
  1041  	// Can be nil when using file writer in wasm
  1042  	if info != nil {
  1043  		if req.downloadStorer != nil {
  1044  			err = sys.Files.MkdirAll(filepath.Join(req.workdir, "download"), 0766)
  1045  			if err != nil {
  1046  				return 0, err
  1047  			}
  1048  			progressID := req.progressID()
  1049  			var dp *DownloadProgress
  1050  			if info.Size() > 0 {
  1051  				dp = req.downloadStorer.Load(progressID, int(req.numBlocks))
  1052  			}
  1053  			if dp != nil {
  1054  				req.startBlock = int64(dp.LastWrittenBlock)
  1055  				if req.startBlock > 0 {
  1056  					req.isResume = true
  1057  				}
  1058  			} else {
  1059  				dp = &DownloadProgress{
  1060  					ID:        progressID,
  1061  					numBlocks: int(req.numBlocks),
  1062  				}
  1063  				req.downloadStorer.Save(dp)
  1064  			}
  1065  		}
  1066  	}
  1067  
  1068  	if req.endBlock == 0 || req.endBlock > chunksPerShard {
  1069  		req.endBlock = chunksPerShard
  1070  	}
  1071  
  1072  	if req.startBlock >= req.endBlock {
  1073  		err = errors.New("invalid_block_num", "start block should be less than end block")
  1074  		return 0, err
  1075  	}
  1076  
  1077  	return
  1078  }
  1079  
  1080  type blobberFile struct {
  1081  	validationRoot []byte
  1082  	size           int64
  1083  }
  1084  
  1085  func GetFileRefFromBlobber(allocationID, blobberId, remotePath string) (fRef *fileref.FileRef, err error) {
  1086  	wg := &sync.WaitGroup{}
  1087  	wg.Add(1)
  1088  	blobber, err := GetBlobber(blobberId)
  1089  	if err != nil {
  1090  		return nil, err
  1091  	}
  1092  
  1093  	a, err := GetAllocation(allocationID)
  1094  	if err != nil {
  1095  		return nil, err
  1096  	}
  1097  
  1098  	ctx := context.Background()
  1099  	listReq := &ListRequest{}
  1100  
  1101  	listReq.allocationID = a.ID
  1102  	listReq.allocationTx = a.Tx
  1103  	listReq.sig = a.sig
  1104  	listReq.blobbers = []*blockchain.StorageNode{
  1105  		{ID: string(blobber.ID), Baseurl: blobber.BaseURL},
  1106  	}
  1107  	listReq.fullconsensus = 1
  1108  	listReq.consensusThresh = 1
  1109  	listReq.ctx = ctx
  1110  	listReq.remotefilepath = remotePath
  1111  
  1112  	rspCh := make(chan *fileMetaResponse, 1)
  1113  	go listReq.getFileMetaInfoFromBlobber(listReq.blobbers[0], 0, rspCh)
  1114  	resp := <-rspCh
  1115  	return resp.fileref, resp.err
  1116  }
  1117  
  1118  func (req *DownloadRequest) getFileRef() (fRef *fileref.FileRef, err error) {
  1119  	listReq := &ListRequest{
  1120  		remotefilepath:     req.remotefilepath,
  1121  		remotefilepathhash: req.remotefilepathhash,
  1122  		allocationID:       req.allocationID,
  1123  		allocationTx:       req.allocationTx,
  1124  		sig:                req.sig,
  1125  		blobbers:           req.blobbers,
  1126  		authToken:          req.authTicket,
  1127  		Consensus: Consensus{
  1128  			RWMutex:         &sync.RWMutex{},
  1129  			fullconsensus:   req.fullconsensus,
  1130  			consensusThresh: req.consensusThresh,
  1131  		},
  1132  		ctx: req.ctx,
  1133  	}
  1134  
  1135  	fMetaResp := listReq.getFileMetaFromBlobbers()
  1136  
  1137  	fRef, err = req.getFileMetaConsensus(fMetaResp)
  1138  	if err != nil {
  1139  		return
  1140  	}
  1141  
  1142  	if fRef.Type == fileref.DIRECTORY {
  1143  		err = errors.New("invalid_operation", "cannot download directory")
  1144  		return nil, err
  1145  	}
  1146  	return
  1147  }
  1148  
  1149  // getFileMetaConsensus will verify actual file hash signature and take consensus in it.
  1150  // Then it will use the signature to calculation validation root signature and verify signature
  1151  // of validation root send by the blobber.
  1152  func (req *DownloadRequest) getFileMetaConsensus(fMetaResp []*fileMetaResponse) (*fileref.FileRef, error) {
  1153  	var selected *fileMetaResponse
  1154  	foundMask := zboxutil.NewUint128(0)
  1155  	req.consensus = 0
  1156  	retMap := make(map[string]int)
  1157  	for _, fmr := range fMetaResp {
  1158  		if fmr.err != nil || fmr.fileref == nil {
  1159  			continue
  1160  		}
  1161  		actualHash := fmr.fileref.ActualFileHash
  1162  		actualFileHashSignature := fmr.fileref.ActualFileHashSignature
  1163  
  1164  		isValid, err := sys.VerifyWith(
  1165  			req.allocOwnerPubKey,
  1166  			actualFileHashSignature,
  1167  			actualHash,
  1168  		)
  1169  		if err != nil {
  1170  			l.Logger.Error(err)
  1171  			continue
  1172  		}
  1173  		if !isValid {
  1174  			l.Logger.Error("invalid signature")
  1175  			continue
  1176  		}
  1177  
  1178  		retMap[actualFileHashSignature]++
  1179  		if retMap[actualFileHashSignature] > req.consensus {
  1180  			req.consensus = retMap[actualFileHashSignature]
  1181  		}
  1182  		if req.isConsensusOk() {
  1183  			selected = fmr
  1184  			break
  1185  		}
  1186  	}
  1187  
  1188  	if selected == nil {
  1189  		l.Logger.Error("File consensus not found for ", req.remotefilepath)
  1190  		return nil, errors.New("consensus_not_met", "")
  1191  	}
  1192  
  1193  	req.validationRootMap = make(map[string]*blobberFile)
  1194  	blobberCount := 0
  1195  	countThreshold := req.consensusThresh + 1
  1196  	if countThreshold > req.fullconsensus {
  1197  		countThreshold = req.consensusThresh
  1198  	}
  1199  	if req.freeRead {
  1200  		countThreshold = req.fullconsensus
  1201  	}
  1202  	for i := 0; i < len(fMetaResp); i++ {
  1203  		fmr := fMetaResp[i]
  1204  		if fmr.err != nil || fmr.fileref == nil {
  1205  			continue
  1206  		}
  1207  		fRef := fmr.fileref
  1208  
  1209  		if selected.fileref.ActualFileHashSignature != fRef.ActualFileHashSignature {
  1210  			continue
  1211  		}
  1212  		if !req.isEnterprise {
  1213  			isValid, err := sys.VerifyWith(
  1214  				req.allocOwnerPubKey,
  1215  				fRef.ValidationRootSignature,
  1216  				fRef.ActualFileHashSignature+fRef.ValidationRoot,
  1217  			)
  1218  			if err != nil {
  1219  				l.Logger.Error(err, "allocOwnerPubKey: ", req.allocOwnerPubKey, " validationRootSignature: ", fRef.ValidationRootSignature, " actualFileHashSignature: ", fRef.ActualFileHashSignature, " validationRoot: ", fRef.ValidationRoot)
  1220  				continue
  1221  			}
  1222  			if !isValid {
  1223  				l.Logger.Error("invalid validation root signature")
  1224  				continue
  1225  			}
  1226  
  1227  			blobber := req.blobbers[fmr.blobberIdx]
  1228  			vr, _ := hex.DecodeString(fmr.fileref.ValidationRoot)
  1229  			req.validationRootMap[blobber.ID] = &blobberFile{
  1230  				size:           fmr.fileref.Size,
  1231  				validationRoot: vr,
  1232  			}
  1233  		}
  1234  		shift := zboxutil.NewUint128(1).Lsh(uint64(fmr.blobberIdx))
  1235  		foundMask = foundMask.Or(shift)
  1236  		req.downloadQueue[fmr.blobberIdx] = downloadPriority{
  1237  			blobberIdx: fmr.blobberIdx,
  1238  			timeTaken:  60000,
  1239  		}
  1240  		blobberCount++
  1241  		if blobberCount == countThreshold {
  1242  			break
  1243  		}
  1244  	}
  1245  	req.consensus = foundMask.CountOnes()
  1246  	if !req.isConsensusOk() {
  1247  		return nil, fmt.Errorf("consensus_not_met")
  1248  	}
  1249  	req.downloadMask = foundMask
  1250  	sort.Slice(req.downloadQueue, req.downloadQueue.Less)
  1251  	return selected.fileref, nil
  1252  }
  1253  
  1254  func (req *DownloadRequest) processDownloadRequest() {
  1255  	remotePathCB := req.remotefilepath
  1256  	if remotePathCB == "" {
  1257  		remotePathCB = req.remotefilepathhash
  1258  	}
  1259  	if req.startBlock < 0 || req.endBlock < 0 {
  1260  		req.errorCB(
  1261  			fmt.Errorf("start block or end block or both cannot be negative."), remotePathCB,
  1262  		)
  1263  		return
  1264  	}
  1265  	fRef, err := req.getFileRef()
  1266  	if err != nil {
  1267  		logger.Logger.Error(err.Error())
  1268  		req.errorCB(
  1269  			fmt.Errorf("Error while getting file ref. Error: %v",
  1270  				err), remotePathCB)
  1271  
  1272  		return
  1273  	}
  1274  	req.fRef = fRef
  1275  	chunksPerShard, err := req.calculateShardsParams(fRef)
  1276  	if err != nil {
  1277  		logger.Logger.Error(err.Error())
  1278  		req.errorCB(
  1279  			fmt.Errorf("Error while calculating shard params. Error: %v",
  1280  				err), remotePathCB)
  1281  		return
  1282  	}
  1283  	req.chunksPerShard = chunksPerShard
  1284  	startBlock, endBlock := req.startBlock, req.endBlock
  1285  	// remainingSize should be calculated based on startBlock number
  1286  	// otherwise end data will have null bytes.
  1287  	remainingSize := req.size - startBlock*int64(req.effectiveBlockSize)
  1288  
  1289  	var wantSize int64
  1290  	if endBlock*int64(req.effectiveBlockSize) < req.size {
  1291  		wantSize = endBlock*int64(req.effectiveBlockSize) - startBlock*int64(req.effectiveBlockSize)
  1292  	} else {
  1293  		wantSize = remainingSize
  1294  	}
  1295  
  1296  	if remainingSize <= 0 {
  1297  		logger.Logger.Error("Nothing to download")
  1298  		req.errorCB(
  1299  			fmt.Errorf("Size to download is %d. Nothing to download", remainingSize), remotePathCB,
  1300  		)
  1301  		return
  1302  	}
  1303  
  1304  	blocksPerShard := (wantSize + int64(req.effectiveBlockSize) - 1) / int64(req.effectiveBlockSize)
  1305  	req.blocksPerShard = blocksPerShard
  1306  }
  1307  
  1308  func (req *DownloadRequest) Seek(offset int64, whence int) (int64, error) {
  1309  	switch whence {
  1310  	case io.SeekStart:
  1311  		if offset >= req.size {
  1312  			return 0, errors.New(ExceededMaxOffsetValue, "file is already downloaded")
  1313  		}
  1314  		req.offset = offset
  1315  	case io.SeekCurrent:
  1316  		if req.offset+offset >= req.size {
  1317  			return 0, errors.New(ExceededMaxOffsetValue, "")
  1318  		}
  1319  		req.offset += offset
  1320  	case io.SeekEnd:
  1321  		newOffset := req.size - offset
  1322  		if newOffset < 0 {
  1323  			return 0, errors.New(NegativeOffsetResultantValue, "")
  1324  		}
  1325  		req.offset = offset
  1326  	default:
  1327  		return 0, errors.New(InvalidWhenceValue,
  1328  			fmt.Sprintf("expected 0, 1 or 2, provided %d", whence))
  1329  	}
  1330  	return req.offset, nil
  1331  }
  1332  
  1333  func writeData(dest io.Writer, data [][][]byte, dataShards, remaining int) (int, error) {
  1334  	total := 0
  1335  	if len(data) == 0 {
  1336  		return 0, errors.New(InvalidWhenceValue, "data cannot be empty")
  1337  	}
  1338  	if dest == nil {
  1339  		return 0, errors.New(InvalidWhenceValue, "destination writer cannot be nil")
  1340  	}
  1341  	for i := 0; i < len(data); i++ {
  1342  		for j := 0; j < dataShards; j++ {
  1343  			if len(data[i][j]) <= remaining {
  1344  				n, err := dest.Write(data[i][j])
  1345  				total += n
  1346  				if err != nil {
  1347  					return total, err
  1348  				}
  1349  			} else {
  1350  				n, err := dest.Write(data[i][j][:remaining])
  1351  				total += n
  1352  				if err != nil {
  1353  					return total, err
  1354  				}
  1355  			}
  1356  			remaining -= len(data[i][j])
  1357  			if remaining <= 0 {
  1358  				return total, nil
  1359  			}
  1360  		}
  1361  	}
  1362  	return total, nil
  1363  }
  1364  
  1365  func writeAtData(dest io.WriterAt, data [][][]byte, dataShards int, offset int64, lastBlock int) (int, error) {
  1366  	var total int
  1367  	for i := 0; i < len(data); i++ {
  1368  		for j := 0; j < dataShards; j++ {
  1369  			if lastBlock != -1 {
  1370  				if len(data[i][j]) <= lastBlock {
  1371  					n, err := dest.WriteAt(data[i][j], offset+int64(total))
  1372  					total += n
  1373  					if err != nil {
  1374  						logger.Logger.Error("writeAt failed: ", err, " offset: ", offset, " total: ", total, "toWriteData: ", len(data[i][j]), " lastBlock: ", lastBlock)
  1375  						return total, err
  1376  					}
  1377  				} else {
  1378  					n, err := dest.WriteAt(data[i][j][:lastBlock], offset+int64(total))
  1379  					total += n
  1380  					if err != nil {
  1381  						logger.Logger.Error("writeAt failed: ", err, " offset: ", offset, " total: ", total, "toWriteData: ", len(data[i][j]), " lastBlock: ", lastBlock)
  1382  						return total, err
  1383  					}
  1384  				}
  1385  				lastBlock -= len(data[i][j])
  1386  				if lastBlock <= 0 {
  1387  					return total, nil
  1388  				}
  1389  			} else {
  1390  				n, err := dest.WriteAt(data[i][j], offset+int64(total))
  1391  				total += n
  1392  				if err != nil {
  1393  					logger.Logger.Error("writeAt failed: ", err, " offset: ", offset, " total: ", total)
  1394  					return total, err
  1395  				}
  1396  			}
  1397  		}
  1398  	}
  1399  	return total, nil
  1400  }
  1401  
  1402  func (dr *DownloadRequest) progressID() string {
  1403  
  1404  	if len(dr.allocationID) > 8 {
  1405  		return filepath.Join(dr.workdir, "download", "d"+dr.allocationID[:8]+"_"+dr.fRef.MetaID())
  1406  	}
  1407  
  1408  	return filepath.Join(dr.workdir, "download", dr.allocationID+"_"+dr.fRef.MetaID())
  1409  }