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 }