github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/renter/contractor/editor.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  	"github.com/NebulousLabs/Sia/types"
    10  )
    11  
    12  // An Editor modifies a Contract by communicating with a host. It uses the
    13  // contract revision protocol to send modification requests to the host.
    14  // Editors are the means by which the renter uploads data to hosts.
    15  type Editor interface {
    16  	// Upload revises the underlying contract to store the new data. It
    17  	// returns the Merkle root of the data.
    18  	Upload(data []byte) (root crypto.Hash, err error)
    19  
    20  	// Delete removes a sector from the underlying contract.
    21  	Delete(crypto.Hash) error
    22  
    23  	// Modify overwrites a sector with new data. Because the Editor does not
    24  	// have access to the original sector data, the new Merkle root must be
    25  	// supplied by the caller.
    26  	Modify(oldRoot, newRoot crypto.Hash, offset uint64, newData []byte) error
    27  
    28  	// Address returns the address of the host.
    29  	Address() modules.NetAddress
    30  
    31  	// ContractID returns the FileContractID of the contract.
    32  	ContractID() types.FileContractID
    33  
    34  	// EndHeight returns the height at which the contract ends.
    35  	EndHeight() types.BlockHeight
    36  
    37  	// Close terminates the connection to the host.
    38  	Close() error
    39  }
    40  
    41  // A hostEditor modifies a Contract by calling the revise RPC on a host. It
    42  // implements the Editor interface. hostEditors are NOT thread-safe; calls to
    43  // Upload must happen in serial.
    44  type hostEditor struct {
    45  	editor     *proto.Editor
    46  	contract   modules.RenterContract
    47  	contractor *Contractor
    48  }
    49  
    50  // Address returns the NetAddress of the host.
    51  func (he *hostEditor) Address() modules.NetAddress { return he.contract.NetAddress }
    52  
    53  // ContractID returns the ID of the contract being revised.
    54  func (he *hostEditor) ContractID() types.FileContractID { return he.contract.ID }
    55  
    56  // EndHeight returns the height at which the host is no longer obligated to
    57  // store the file.
    58  func (he *hostEditor) EndHeight() types.BlockHeight { return he.contract.FileContract.WindowStart }
    59  
    60  // Close cleanly terminates the revision loop with the host and closes the
    61  // connection.
    62  func (he *hostEditor) Close() error { return he.editor.Close() }
    63  
    64  // Upload negotiates a revision that adds a sector to a file contract.
    65  func (he *hostEditor) Upload(data []byte) (crypto.Hash, error) {
    66  	oldUploadSpending := he.editor.UploadSpending
    67  	oldStorageSpending := he.editor.StorageSpending
    68  	contract, sectorRoot, err := he.editor.Upload(data)
    69  	if err != nil {
    70  		return crypto.Hash{}, err
    71  	}
    72  	uploadDelta := he.editor.UploadSpending.Sub(oldUploadSpending)
    73  	storageDelta := he.editor.StorageSpending.Sub(oldStorageSpending)
    74  
    75  	he.contractor.mu.Lock()
    76  	he.contractor.uploadSpending = he.contractor.uploadSpending.Add(uploadDelta)
    77  	he.contractor.storageSpending = he.contractor.storageSpending.Add(storageDelta)
    78  	he.contractor.contracts[contract.ID] = contract
    79  	he.contractor.saveSync()
    80  	he.contractor.mu.Unlock()
    81  	he.contract = contract
    82  
    83  	return sectorRoot, nil
    84  }
    85  
    86  // Delete negotiates a revision that removes a sector from a file contract.
    87  func (he *hostEditor) Delete(root crypto.Hash) error {
    88  	contract, err := he.editor.Delete(root)
    89  	if err != nil {
    90  		return err
    91  	}
    92  
    93  	he.contractor.mu.Lock()
    94  	he.contractor.contracts[contract.ID] = contract
    95  	he.contractor.saveSync()
    96  	he.contractor.mu.Unlock()
    97  	he.contract = contract
    98  
    99  	return nil
   100  }
   101  
   102  // Modify negotiates a revision that edits a sector in a file contract.
   103  func (he *hostEditor) Modify(oldRoot, newRoot crypto.Hash, offset uint64, newData []byte) error {
   104  	oldUploadSpending := he.editor.UploadSpending
   105  	contract, err := he.editor.Modify(oldRoot, newRoot, offset, newData)
   106  	if err != nil {
   107  		return err
   108  	}
   109  	uploadDelta := he.editor.UploadSpending.Sub(oldUploadSpending)
   110  
   111  	he.contractor.mu.Lock()
   112  	he.contractor.uploadSpending = he.contractor.uploadSpending.Add(uploadDelta)
   113  	he.contractor.contracts[contract.ID] = contract
   114  	he.contractor.saveSync()
   115  	he.contractor.mu.Unlock()
   116  	he.contract = contract
   117  
   118  	return nil
   119  }
   120  
   121  // Editor initiates the contract revision process with a host, and returns
   122  // an Editor.
   123  func (c *Contractor) Editor(contract modules.RenterContract) (Editor, error) {
   124  	c.mu.RLock()
   125  	height := c.blockHeight
   126  	c.mu.RUnlock()
   127  	if height > contract.FileContract.WindowStart {
   128  		return nil, errors.New("contract has already ended")
   129  	}
   130  	host, ok := c.hdb.Host(contract.NetAddress)
   131  	if !ok {
   132  		return nil, errors.New("no record of that host")
   133  	}
   134  	if host.StoragePrice.Cmp(maxStoragePrice) > 0 {
   135  		return nil, errTooExpensive
   136  	}
   137  
   138  	// create editor
   139  	e, err := proto.NewEditor(host, contract, height)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  
   144  	return &hostEditor{
   145  		editor:     e,
   146  		contractor: c,
   147  	}, nil
   148  }