github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/renter/contractor/downloader.go (about)

     1  package contractor
     2  
     3  import (
     4  	"errors"
     5  
     6  	"github.com/NebulousLabs/Sia/crypto"
     7  	"github.com/NebulousLabs/Sia/modules"
     8  	"github.com/NebulousLabs/Sia/modules/renter/proto"
     9  )
    10  
    11  // An Downloader retrieves sectors from with a host. It requests one sector at
    12  // a time, and revises the file contract to transfer money to the host
    13  // proportional to the data retrieved.
    14  type Downloader interface {
    15  	// Sector retrieves the sector with the specified Merkle root, and revises
    16  	// the underlying contract to pay the host proportionally to the data
    17  	// retrieve.
    18  	Sector(root crypto.Hash) ([]byte, error)
    19  
    20  	// Close terminates the connection to the host.
    21  	Close() error
    22  }
    23  
    24  // A hostDownloader retrieves sectors by calling the download RPC on a host.
    25  // It implements the Downloader interface. hostDownloaders are NOT thread-
    26  // safe; calls to Sector must be serialized.
    27  type hostDownloader struct {
    28  	downloader *proto.Downloader
    29  	contractor *Contractor
    30  }
    31  
    32  // Sector retrieves the sector with the specified Merkle root, and revises
    33  // the underlying contract to pay the host proportionally to the data
    34  // retrieve.
    35  func (hd *hostDownloader) Sector(root crypto.Hash) ([]byte, error) {
    36  	oldSpending := hd.downloader.DownloadSpending
    37  	contract, sector, err := hd.downloader.Sector(root)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  	delta := hd.downloader.DownloadSpending.Sub(oldSpending)
    42  
    43  	hd.contractor.mu.Lock()
    44  	hd.contractor.downloadSpending = hd.contractor.downloadSpending.Add(delta)
    45  	hd.contractor.contracts[contract.ID] = contract
    46  	hd.contractor.saveSync()
    47  	hd.contractor.mu.Unlock()
    48  
    49  	return sector, nil
    50  }
    51  
    52  // Close cleanly terminates the download loop with the host and closes the
    53  // connection.
    54  func (hd *hostDownloader) Close() error { return hd.downloader.Close() }
    55  
    56  // Downloader initiates the download request loop with a host, and returns a
    57  // Downloader.
    58  func (c *Contractor) Downloader(contract modules.RenterContract) (Downloader, error) {
    59  	c.mu.RLock()
    60  	height := c.blockHeight
    61  	c.mu.RUnlock()
    62  	if height > contract.FileContract.WindowStart {
    63  		return nil, errors.New("contract has already ended")
    64  	}
    65  	host, ok := c.hdb.Host(contract.NetAddress)
    66  	if !ok {
    67  		return nil, errors.New("no record of that host")
    68  	}
    69  	if host.DownloadBandwidthPrice.Cmp(maxDownloadPrice) > 0 {
    70  		return nil, errTooExpensive
    71  	}
    72  
    73  	// create downloader
    74  	d, err := proto.NewDownloader(host, contract)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	return &hostDownloader{
    80  		downloader: d,
    81  		contractor: c,
    82  	}, nil
    83  }