gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/renter/proto/session.go (about)

     1  package proto
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/cipher"
     6  	"encoding/json"
     7  	"io"
     8  	"math/bits"
     9  	"net"
    10  	"sort"
    11  	"sync"
    12  	"time"
    13  
    14  	"gitlab.com/NebulousLabs/errors"
    15  	"gitlab.com/NebulousLabs/ratelimit"
    16  
    17  	"gitlab.com/SiaPrime/SiaPrime/build"
    18  	"gitlab.com/SiaPrime/SiaPrime/crypto"
    19  	"gitlab.com/SiaPrime/SiaPrime/modules"
    20  	"gitlab.com/SiaPrime/SiaPrime/types"
    21  )
    22  
    23  // A Session is an ongoing exchange of RPCs via the renter-host protocol.
    24  //
    25  // TODO: The session type needs access to a logger. Probably the renter logger.
    26  type Session struct {
    27  	aead        cipher.AEAD
    28  	challenge   [16]byte
    29  	closeChan   chan struct{}
    30  	conn        net.Conn
    31  	contractID  types.FileContractID
    32  	contractSet *ContractSet
    33  	deps        modules.Dependencies
    34  	hdb         hostDB
    35  	height      types.BlockHeight
    36  	host        modules.HostDBEntry
    37  	once        sync.Once
    38  }
    39  
    40  // writeRequest sends an encrypted RPC request to the host.
    41  func (s *Session) writeRequest(rpcID types.Specifier, req interface{}) error {
    42  	return modules.WriteRPCRequest(s.conn, s.aead, rpcID, req)
    43  }
    44  
    45  // writeResponse writes an encrypted RPC response to the host.
    46  func (s *Session) writeResponse(resp interface{}, err error) error {
    47  	return modules.WriteRPCResponse(s.conn, s.aead, resp, err)
    48  }
    49  
    50  // readResponse reads an encrypted RPC response from the host.
    51  func (s *Session) readResponse(resp interface{}, maxLen uint64) error {
    52  	return modules.ReadRPCResponse(s.conn, s.aead, resp, maxLen)
    53  }
    54  
    55  // call is a helper method that calls writeRequest followed by readResponse.
    56  func (s *Session) call(rpcID types.Specifier, req, resp interface{}, maxLen uint64) error {
    57  	if err := s.writeRequest(rpcID, req); err != nil {
    58  		return err
    59  	}
    60  	return s.readResponse(resp, maxLen)
    61  }
    62  
    63  // Lock calls the Lock RPC, locking the supplied contract and returning its
    64  // most recent revision.
    65  func (s *Session) Lock(id types.FileContractID, secretKey crypto.SecretKey) (types.FileContractRevision, []types.TransactionSignature, error) {
    66  	sig := crypto.SignHash(crypto.HashAll(modules.RPCChallengePrefix, s.challenge), secretKey)
    67  	req := modules.LoopLockRequest{
    68  		ContractID: id,
    69  		Signature:  sig[:],
    70  		Timeout:    defaultContractLockTimeout,
    71  	}
    72  
    73  	timeoutDur := time.Duration(defaultContractLockTimeout) * time.Millisecond
    74  	extendDeadline(s.conn, modules.NegotiateSettingsTime+timeoutDur)
    75  	var resp modules.LoopLockResponse
    76  	if err := s.call(modules.RPCLoopLock, req, &resp, modules.RPCMinLen); err != nil {
    77  		return types.FileContractRevision{}, nil, err
    78  	}
    79  	// Unconditionally update the challenge.
    80  	s.challenge = resp.NewChallenge
    81  
    82  	if !resp.Acquired {
    83  		return resp.Revision, resp.Signatures, errors.New("contract is locked by another party")
    84  	}
    85  	// Set the new Session contract.
    86  	s.contractID = id
    87  	// Verify the public keys in the claimed revision.
    88  	expectedUnlockConditions := types.UnlockConditions{
    89  		PublicKeys: []types.SiaPublicKey{
    90  			types.Ed25519PublicKey(secretKey.PublicKey()),
    91  			s.host.PublicKey,
    92  		},
    93  		SignaturesRequired: 2,
    94  	}
    95  	if resp.Revision.UnlockConditions.UnlockHash() != expectedUnlockConditions.UnlockHash() {
    96  		return resp.Revision, resp.Signatures, errors.New("host's claimed revision has wrong unlock conditions")
    97  	}
    98  	// Verify the claimed signatures.
    99  	if err := modules.VerifyFileContractRevisionTransactionSignatures(resp.Revision, resp.Signatures, s.height); err != nil {
   100  		return resp.Revision, resp.Signatures, err
   101  	}
   102  	return resp.Revision, resp.Signatures, nil
   103  }
   104  
   105  // Unlock calls the Unlock RPC, unlocking the currently-locked contract.
   106  func (s *Session) Unlock() error {
   107  	if s.contractID == (types.FileContractID{}) {
   108  		return errors.New("no contract locked")
   109  	}
   110  	extendDeadline(s.conn, modules.NegotiateSettingsTime)
   111  	return s.writeRequest(modules.RPCLoopUnlock, nil)
   112  }
   113  
   114  // Settings calls the Settings RPC, returning the host's reported settings.
   115  func (s *Session) Settings() (modules.HostExternalSettings, error) {
   116  	extendDeadline(s.conn, modules.NegotiateSettingsTime)
   117  	var resp modules.LoopSettingsResponse
   118  	if err := s.call(modules.RPCLoopSettings, nil, &resp, modules.RPCMinLen); err != nil {
   119  		return modules.HostExternalSettings{}, err
   120  	}
   121  	if err := json.Unmarshal(resp.Settings, &s.host.HostExternalSettings); err != nil {
   122  		return modules.HostExternalSettings{}, err
   123  	}
   124  	return s.host.HostExternalSettings, nil
   125  }
   126  
   127  // Append calls the Write RPC with a single Append action, returning the
   128  // updated contract and the Merkle root of the appended sector.
   129  func (s *Session) Append(data []byte) (_ modules.RenterContract, _ crypto.Hash, err error) {
   130  	rc, err := s.Write([]modules.LoopWriteAction{{Type: modules.WriteActionAppend, Data: data}})
   131  	return rc, crypto.MerkleRoot(data), err
   132  }
   133  
   134  // Replace calls the Write RPC with a series of actions that replace the sector
   135  // at the specified index with data, returning the updated contract and the
   136  // Merkle root of the new sector.
   137  func (s *Session) Replace(data []byte, sectorIndex uint64, trim bool) (_ modules.RenterContract, _ crypto.Hash, err error) {
   138  	sc, haveContract := s.contractSet.Acquire(s.contractID)
   139  	if !haveContract {
   140  		return modules.RenterContract{}, crypto.Hash{}, errors.New("contract not present in contract set")
   141  	}
   142  	defer s.contractSet.Return(sc)
   143  	// get current number of sectors
   144  	numSectors := sc.header.LastRevision().NewFileSize / modules.SectorSize
   145  	actions := []modules.LoopWriteAction{
   146  		// append the new sector
   147  		{Type: modules.WriteActionAppend, Data: data},
   148  		// swap the new sector with the old sector
   149  		{Type: modules.WriteActionSwap, A: 0, B: numSectors},
   150  	}
   151  	if trim {
   152  		// delete the old sector
   153  		actions = append(actions, modules.LoopWriteAction{Type: modules.WriteActionTrim, A: 1})
   154  	}
   155  
   156  	rc, err := s.write(sc, actions)
   157  	return rc, crypto.MerkleRoot(data), err
   158  }
   159  
   160  // Write implements the Write RPC, except for ActionUpdate. A Merkle proof is
   161  // always requested.
   162  func (s *Session) Write(actions []modules.LoopWriteAction) (_ modules.RenterContract, err error) {
   163  	sc, haveContract := s.contractSet.Acquire(s.contractID)
   164  	if !haveContract {
   165  		return modules.RenterContract{}, errors.New("contract not present in contract set")
   166  	}
   167  	defer s.contractSet.Return(sc)
   168  	return s.write(sc, actions)
   169  }
   170  
   171  func (s *Session) write(sc *SafeContract, actions []modules.LoopWriteAction) (_ modules.RenterContract, err error) {
   172  	contract := sc.header // for convenience
   173  
   174  	// calculate price per sector
   175  	blockBytes := types.NewCurrency64(modules.SectorSize * uint64(contract.LastRevision().NewWindowEnd-s.height))
   176  	sectorBandwidthPrice := s.host.UploadBandwidthPrice.Mul64(modules.SectorSize)
   177  	sectorStoragePrice := s.host.StoragePrice.Mul(blockBytes)
   178  	sectorCollateral := s.host.Collateral.Mul(blockBytes)
   179  
   180  	// calculate the new Merkle root set and total cost/collateral
   181  	var bandwidthPrice, storagePrice, collateral types.Currency
   182  	newFileSize := contract.LastRevision().NewFileSize
   183  	for _, action := range actions {
   184  		switch action.Type {
   185  		case modules.WriteActionAppend:
   186  			bandwidthPrice = bandwidthPrice.Add(sectorBandwidthPrice)
   187  			newFileSize += modules.SectorSize
   188  
   189  		case modules.WriteActionTrim:
   190  			newFileSize -= modules.SectorSize * action.A
   191  
   192  		case modules.WriteActionSwap:
   193  
   194  		case modules.WriteActionUpdate:
   195  			return modules.RenterContract{}, errors.New("update not supported")
   196  
   197  		default:
   198  			build.Critical("unknown action type", action.Type)
   199  		}
   200  	}
   201  
   202  	if newFileSize > contract.LastRevision().NewFileSize {
   203  		addedSectors := (newFileSize - contract.LastRevision().NewFileSize) / modules.SectorSize
   204  		storagePrice = sectorStoragePrice.Mul64(addedSectors)
   205  		collateral = sectorCollateral.Mul64(addedSectors)
   206  	}
   207  
   208  	// estimate cost of Merkle proof
   209  	proofSize := crypto.HashSize * (128 + len(actions))
   210  	bandwidthPrice = bandwidthPrice.Add(s.host.DownloadBandwidthPrice.Mul64(uint64(proofSize)))
   211  
   212  	// to mitigate small errors (e.g. differing block heights), fudge the
   213  	// price and collateral by hostPriceLeeway.
   214  	cost := s.host.BaseRPCPrice.Add(bandwidthPrice).Add(storagePrice).MulFloat(1 + hostPriceLeeway)
   215  	collateral = collateral.MulFloat(1 - hostPriceLeeway)
   216  
   217  	// check that enough funds are available
   218  	if contract.RenterFunds().Cmp(cost) < 0 {
   219  		return modules.RenterContract{}, errors.New("contract has insufficient funds to support upload")
   220  	}
   221  	if contract.LastRevision().NewMissedProofOutputs[1].Value.Cmp(collateral) < 0 {
   222  		// The contract doesn't have enough value in it to supply the
   223  		// collateral. Instead of giving up, have the host put up everything
   224  		// that remains, even if that is zero. The renter was aware when the
   225  		// contract was formed that there may not be enough collateral in the
   226  		// contract to cover the full storage needs for the renter, yet the
   227  		// renter formed the contract anyway. Therefore the renter must think
   228  		// that it's sufficient.
   229  		//
   230  		// TODO: log.Debugln here to indicate that the host is having issues
   231  		// supplying collateral.
   232  		//
   233  		// TODO: We may in the future want to have the renter perform a renewal
   234  		// on this contract so that the host can refill the collateral. That's a
   235  		// concern at the renter level though, not at the session level. The
   236  		// session should still be assuming that if the renter is willing to use
   237  		// this contract, the renter is aware that there isn't enough collateral
   238  		// remaining and is happy to use the contract anyway. Therefore this
   239  		// TODO should be moved to a different part of the codebase.
   240  		collateral = contract.LastRevision().NewMissedProofOutputs[1].Value
   241  	}
   242  
   243  	// create the revision; we will update the Merkle root later
   244  	rev := newRevision(contract.LastRevision(), cost)
   245  	rev.NewMissedProofOutputs[1].Value = rev.NewMissedProofOutputs[1].Value.Sub(collateral)
   246  	rev.NewMissedProofOutputs[2].Value = rev.NewMissedProofOutputs[2].Value.Add(collateral)
   247  	rev.NewFileSize = newFileSize
   248  
   249  	// create the request
   250  	req := modules.LoopWriteRequest{
   251  		Actions:           actions,
   252  		MerkleProof:       true,
   253  		NewRevisionNumber: rev.NewRevisionNumber,
   254  	}
   255  	req.NewValidProofValues = make([]types.Currency, len(rev.NewValidProofOutputs))
   256  	for i, o := range rev.NewValidProofOutputs {
   257  		req.NewValidProofValues[i] = o.Value
   258  	}
   259  	req.NewMissedProofValues = make([]types.Currency, len(rev.NewMissedProofOutputs))
   260  	for i, o := range rev.NewMissedProofOutputs {
   261  		req.NewMissedProofValues[i] = o.Value
   262  	}
   263  
   264  	// record the change we are about to make to the contract. If we lose power
   265  	// mid-revision, this allows us to restore either the pre-revision or
   266  	// post-revision contract.
   267  	//
   268  	// TODO: update this for non-local root storage
   269  	walTxn, err := sc.managedRecordUploadIntent(rev, crypto.Hash{}, storagePrice, bandwidthPrice)
   270  	if err != nil {
   271  		return modules.RenterContract{}, err
   272  	}
   273  
   274  	defer func() {
   275  		// Increase Successful/Failed interactions accordingly
   276  		if err != nil {
   277  			s.hdb.IncrementFailedInteractions(s.host.PublicKey)
   278  		} else {
   279  			s.hdb.IncrementSuccessfulInteractions(s.host.PublicKey)
   280  		}
   281  
   282  		// reset deadline
   283  		extendDeadline(s.conn, time.Hour)
   284  	}()
   285  
   286  	// Disrupt here before sending the signed revision to the host.
   287  	if s.deps.Disrupt("InterruptUploadBeforeSendingRevision") {
   288  		return modules.RenterContract{}, errors.New("InterruptUploadBeforeSendingRevision disrupt")
   289  	}
   290  
   291  	// send Write RPC request
   292  	extendDeadline(s.conn, modules.NegotiateFileContractRevisionTime)
   293  	if err := s.writeRequest(modules.RPCLoopWrite, req); err != nil {
   294  		return modules.RenterContract{}, err
   295  	}
   296  
   297  	// read Merkle proof from host
   298  	var merkleResp modules.LoopWriteMerkleProof
   299  	if err := s.readResponse(&merkleResp, modules.RPCMinLen); err != nil {
   300  		return modules.RenterContract{}, err
   301  	}
   302  	// verify the proof, first by verifying the old Merkle root...
   303  	numSectors := contract.LastRevision().NewFileSize / modules.SectorSize
   304  	proofRanges := calculateProofRanges(actions, numSectors)
   305  	proofHashes := merkleResp.OldSubtreeHashes
   306  	leafHashes := merkleResp.OldLeafHashes
   307  	oldRoot, newRoot := contract.LastRevision().NewFileMerkleRoot, merkleResp.NewMerkleRoot
   308  	if !crypto.VerifyDiffProof(proofRanges, numSectors, proofHashes, leafHashes, oldRoot) {
   309  		return modules.RenterContract{}, errors.New("invalid Merkle proof for old root")
   310  	}
   311  	// ...then by modifying the leaves and verifying the new Merkle root
   312  	leafHashes = modifyLeaves(leafHashes, actions, numSectors)
   313  	proofRanges = modifyProofRanges(proofRanges, actions, numSectors)
   314  	if !crypto.VerifyDiffProof(proofRanges, numSectors, proofHashes, leafHashes, newRoot) {
   315  		return modules.RenterContract{}, errors.New("invalid Merkle proof for new root")
   316  	}
   317  
   318  	// update the revision, sign it, and send it
   319  	rev.NewFileMerkleRoot = newRoot
   320  	txn := types.Transaction{
   321  		FileContractRevisions: []types.FileContractRevision{rev},
   322  		TransactionSignatures: []types.TransactionSignature{
   323  			{
   324  				ParentID:       crypto.Hash(rev.ParentID),
   325  				CoveredFields:  types.CoveredFields{FileContractRevisions: []uint64{0}},
   326  				PublicKeyIndex: 0, // renter key is always first -- see formContract
   327  			},
   328  			{
   329  				ParentID:       crypto.Hash(rev.ParentID),
   330  				PublicKeyIndex: 1,
   331  				CoveredFields:  types.CoveredFields{FileContractRevisions: []uint64{0}},
   332  				Signature:      nil, // to be provided by host
   333  			},
   334  		},
   335  	}
   336  	sig := crypto.SignHash(txn.SigHash(0, s.height), contract.SecretKey)
   337  	txn.TransactionSignatures[0].Signature = sig[:]
   338  	renterSig := modules.LoopWriteResponse{
   339  		Signature: sig[:],
   340  	}
   341  	if err := s.writeResponse(renterSig, nil); err != nil {
   342  		return modules.RenterContract{}, err
   343  	}
   344  
   345  	// read the host's signature
   346  	var hostSig modules.LoopWriteResponse
   347  	if err := s.readResponse(&hostSig, modules.RPCMinLen); err != nil {
   348  		// If the host was OOS, we update the contract utility.
   349  		if modules.IsOOSErr(err) {
   350  			u := sc.Utility()
   351  			u.GoodForUpload = false // Stop uploading to such a host immediately.
   352  			u.LastOOSErr = s.height
   353  			err = errors.Compose(err, sc.UpdateUtility(u))
   354  		}
   355  		return modules.RenterContract{}, err
   356  	}
   357  	txn.TransactionSignatures[1].Signature = hostSig.Signature
   358  
   359  	// Disrupt here before updating the contract.
   360  	if s.deps.Disrupt("InterruptUploadAfterSendingRevision") {
   361  		return modules.RenterContract{}, errors.New("InterruptUploadAfterSendingRevision disrupt")
   362  	}
   363  
   364  	// update contract
   365  	//
   366  	// TODO: unnecessary?
   367  	err = sc.managedCommitUpload(walTxn, txn, crypto.Hash{}, storagePrice, bandwidthPrice)
   368  	if err != nil {
   369  		return modules.RenterContract{}, err
   370  	}
   371  	return sc.Metadata(), nil
   372  }
   373  
   374  // Read calls the Read RPC, writing the requested data to w. The RPC can be
   375  // cancelled (with a granularity of one section) via the cancel channel.
   376  func (s *Session) Read(w io.Writer, req modules.LoopReadRequest, cancel <-chan struct{}) (_ modules.RenterContract, err error) {
   377  	// Reset deadline when finished.
   378  	defer extendDeadline(s.conn, time.Hour)
   379  
   380  	// Sanity-check the request.
   381  	for _, sec := range req.Sections {
   382  		if uint64(sec.Offset)+uint64(sec.Length) > modules.SectorSize {
   383  			return modules.RenterContract{}, errors.New("illegal offset and/or length")
   384  		}
   385  		if req.MerkleProof {
   386  			if sec.Offset%crypto.SegmentSize != 0 || sec.Length%crypto.SegmentSize != 0 {
   387  				return modules.RenterContract{}, errors.New("offset and length must be multiples of SegmentSize when requesting a Merkle proof")
   388  			}
   389  		}
   390  	}
   391  
   392  	// Acquire the contract.
   393  	sc, haveContract := s.contractSet.Acquire(s.contractID)
   394  	if !haveContract {
   395  		return modules.RenterContract{}, errors.New("contract not present in contract set")
   396  	}
   397  	defer s.contractSet.Return(sc)
   398  	contract := sc.header // for convenience
   399  
   400  	// calculate estimated bandwidth
   401  	var totalLength uint64
   402  	for _, sec := range req.Sections {
   403  		totalLength += uint64(sec.Length)
   404  	}
   405  	var estProofHashes uint64
   406  	if req.MerkleProof {
   407  		// use the worst-case proof size of 2*tree depth (this occurs when
   408  		// proving across the two leaves in the center of the tree)
   409  		estHashesPerProof := 2 * bits.Len64(modules.SectorSize/crypto.SegmentSize)
   410  		estProofHashes = uint64(len(req.Sections) * estHashesPerProof)
   411  	}
   412  	estBandwidth := totalLength + estProofHashes*crypto.HashSize
   413  	if estBandwidth < modules.RPCMinLen {
   414  		estBandwidth = modules.RPCMinLen
   415  	}
   416  	// calculate sector accesses
   417  	sectorAccesses := make(map[crypto.Hash]struct{})
   418  	for _, sec := range req.Sections {
   419  		sectorAccesses[sec.MerkleRoot] = struct{}{}
   420  	}
   421  	// calculate price
   422  	bandwidthPrice := s.host.DownloadBandwidthPrice.Mul64(estBandwidth)
   423  	sectorAccessPrice := s.host.SectorAccessPrice.Mul64(uint64(len(sectorAccesses)))
   424  	price := s.host.BaseRPCPrice.Add(bandwidthPrice).Add(sectorAccessPrice)
   425  	if contract.RenterFunds().Cmp(price) < 0 {
   426  		return modules.RenterContract{}, errors.New("contract has insufficient funds to support download")
   427  	}
   428  	// To mitigate small errors (e.g. differing block heights), fudge the
   429  	// price and collateral by 0.2%.
   430  	price = price.MulFloat(1 + hostPriceLeeway)
   431  
   432  	// create the download revision and sign it
   433  	rev := newDownloadRevision(contract.LastRevision(), price)
   434  	txn := types.Transaction{
   435  		FileContractRevisions: []types.FileContractRevision{rev},
   436  		TransactionSignatures: []types.TransactionSignature{
   437  			{
   438  				ParentID:       crypto.Hash(rev.ParentID),
   439  				CoveredFields:  types.CoveredFields{FileContractRevisions: []uint64{0}},
   440  				PublicKeyIndex: 0, // renter key is always first -- see formContract
   441  			},
   442  			{
   443  				ParentID:       crypto.Hash(rev.ParentID),
   444  				PublicKeyIndex: 1,
   445  				CoveredFields:  types.CoveredFields{FileContractRevisions: []uint64{0}},
   446  				Signature:      nil, // to be provided by host
   447  			},
   448  		},
   449  	}
   450  	sig := crypto.SignHash(txn.SigHash(0, s.height), contract.SecretKey)
   451  	txn.TransactionSignatures[0].Signature = sig[:]
   452  
   453  	req.NewRevisionNumber = rev.NewRevisionNumber
   454  	req.NewValidProofValues = make([]types.Currency, len(rev.NewValidProofOutputs))
   455  	for i, o := range rev.NewValidProofOutputs {
   456  		req.NewValidProofValues[i] = o.Value
   457  	}
   458  	req.NewMissedProofValues = make([]types.Currency, len(rev.NewMissedProofOutputs))
   459  	for i, o := range rev.NewMissedProofOutputs {
   460  		req.NewMissedProofValues[i] = o.Value
   461  	}
   462  	req.Signature = sig[:]
   463  
   464  	// record the change we are about to make to the contract. If we lose power
   465  	// mid-revision, this allows us to restore either the pre-revision or
   466  	// post-revision contract.
   467  	walTxn, err := sc.managedRecordDownloadIntent(rev, price)
   468  	if err != nil {
   469  		return modules.RenterContract{}, err
   470  	}
   471  
   472  	// Increase Successful/Failed interactions accordingly
   473  	defer func() {
   474  		if err != nil {
   475  			s.hdb.IncrementFailedInteractions(contract.HostPublicKey())
   476  		} else {
   477  			s.hdb.IncrementSuccessfulInteractions(contract.HostPublicKey())
   478  		}
   479  	}()
   480  
   481  	// Disrupt before sending the signed revision to the host.
   482  	if s.deps.Disrupt("InterruptDownloadBeforeSendingRevision") {
   483  		return modules.RenterContract{}, errors.New("InterruptDownloadBeforeSendingRevision disrupt")
   484  	}
   485  
   486  	// send request
   487  	extendDeadline(s.conn, modules.NegotiateDownloadTime)
   488  	err = s.writeRequest(modules.RPCLoopRead, req)
   489  	if err != nil {
   490  		return modules.RenterContract{}, err
   491  	}
   492  
   493  	// spawn a goroutine to handle cancellation
   494  	doneChan := make(chan struct{})
   495  	go func() {
   496  		select {
   497  		case <-cancel:
   498  		case <-doneChan:
   499  		}
   500  		s.writeResponse(modules.RPCLoopReadStop, nil)
   501  	}()
   502  	// ensure we send RPCLoopReadStop before returning
   503  	defer close(doneChan)
   504  
   505  	// read responses
   506  	var hostSig []byte
   507  	for _, sec := range req.Sections {
   508  		var resp modules.LoopReadResponse
   509  		err = s.readResponse(&resp, modules.RPCMinLen+uint64(sec.Length))
   510  		if err != nil {
   511  			return modules.RenterContract{}, err
   512  		}
   513  		// The host may have sent data, a signature, or both. If they sent data,
   514  		// validate it.
   515  		if len(resp.Data) > 0 {
   516  			if len(resp.Data) != int(sec.Length) {
   517  				return modules.RenterContract{}, errors.New("host did not send enough sector data")
   518  			}
   519  			if req.MerkleProof {
   520  				proofStart := int(sec.Offset) / crypto.SegmentSize
   521  				proofEnd := int(sec.Offset+sec.Length) / crypto.SegmentSize
   522  				if !crypto.VerifyRangeProof(resp.Data, resp.MerkleProof, proofStart, proofEnd, sec.MerkleRoot) {
   523  					return modules.RenterContract{}, errors.New("host provided incorrect sector data or Merkle proof")
   524  				}
   525  			}
   526  			// write sector data
   527  			if _, err := w.Write(resp.Data); err != nil {
   528  				return modules.RenterContract{}, err
   529  			}
   530  		}
   531  		// If the host sent a signature, exit the loop; they won't be sending
   532  		// any more data
   533  		if len(resp.Signature) > 0 {
   534  			hostSig = resp.Signature
   535  			break
   536  		}
   537  	}
   538  	if hostSig == nil {
   539  		// the host is required to send a signature; if they haven't sent one
   540  		// yet, they should send an empty ReadResponse containing just the
   541  		// signature.
   542  		var resp modules.LoopReadResponse
   543  		err = s.readResponse(&resp, modules.RPCMinLen)
   544  		if err != nil {
   545  			return modules.RenterContract{}, err
   546  		}
   547  		hostSig = resp.Signature
   548  	}
   549  	txn.TransactionSignatures[1].Signature = hostSig
   550  
   551  	// Disrupt before committing.
   552  	if s.deps.Disrupt("InterruptDownloadAfterSendingRevision") {
   553  		return modules.RenterContract{}, errors.New("InterruptDownloadAfterSendingRevision disrupt")
   554  	}
   555  
   556  	// update contract and metrics
   557  	if err := sc.managedCommitDownload(walTxn, txn, price); err != nil {
   558  		return modules.RenterContract{}, err
   559  	}
   560  
   561  	return sc.Metadata(), nil
   562  }
   563  
   564  // ReadSection calls the Read RPC with a single section and returns the
   565  // requested data. A Merkle proof is always requested.
   566  func (s *Session) ReadSection(root crypto.Hash, offset, length uint32) (_ modules.RenterContract, _ []byte, err error) {
   567  	req := modules.LoopReadRequest{
   568  		Sections: []modules.LoopReadRequestSection{{
   569  			MerkleRoot: root,
   570  			Offset:     offset,
   571  			Length:     length,
   572  		}},
   573  		MerkleProof: true,
   574  	}
   575  	var buf bytes.Buffer
   576  	buf.Grow(int(length))
   577  	contract, err := s.Read(&buf, req, nil)
   578  	return contract, buf.Bytes(), err
   579  }
   580  
   581  // SectorRoots calls the contract roots download RPC and returns the requested sector roots. The
   582  // Revision and Signature fields of req are filled in automatically. If a
   583  // Merkle proof is requested, it is verified.
   584  func (s *Session) SectorRoots(req modules.LoopSectorRootsRequest) (_ modules.RenterContract, _ []crypto.Hash, err error) {
   585  	// Reset deadline when finished.
   586  	defer extendDeadline(s.conn, time.Hour)
   587  
   588  	// Acquire the contract.
   589  	sc, haveContract := s.contractSet.Acquire(s.contractID)
   590  	if !haveContract {
   591  		return modules.RenterContract{}, nil, errors.New("contract not present in contract set")
   592  	}
   593  	defer s.contractSet.Return(sc)
   594  	contract := sc.header // for convenience
   595  
   596  	// calculate price
   597  	estProofHashes := bits.Len64(contract.LastRevision().NewFileSize / modules.SectorSize)
   598  	estBandwidth := (uint64(estProofHashes) + req.NumRoots) * crypto.HashSize
   599  	if estBandwidth < modules.RPCMinLen {
   600  		estBandwidth = modules.RPCMinLen
   601  	}
   602  	bandwidthPrice := s.host.DownloadBandwidthPrice.Mul64(estBandwidth)
   603  	price := s.host.BaseRPCPrice.Add(bandwidthPrice)
   604  	if contract.RenterFunds().Cmp(price) < 0 {
   605  		return modules.RenterContract{}, nil, errors.New("contract has insufficient funds to support sector roots download")
   606  	}
   607  	// To mitigate small errors (e.g. differing block heights), fudge the
   608  	// price and collateral by 0.2%.
   609  	price = price.MulFloat(1 + hostPriceLeeway)
   610  
   611  	// create the download revision and sign it
   612  	rev := newDownloadRevision(contract.LastRevision(), price)
   613  	txn := types.Transaction{
   614  		FileContractRevisions: []types.FileContractRevision{rev},
   615  		TransactionSignatures: []types.TransactionSignature{
   616  			{
   617  				ParentID:       crypto.Hash(rev.ParentID),
   618  				CoveredFields:  types.CoveredFields{FileContractRevisions: []uint64{0}},
   619  				PublicKeyIndex: 0, // renter key is always first -- see formContract
   620  			},
   621  			{
   622  				ParentID:       crypto.Hash(rev.ParentID),
   623  				PublicKeyIndex: 1,
   624  				CoveredFields:  types.CoveredFields{FileContractRevisions: []uint64{0}},
   625  				Signature:      nil, // to be provided by host
   626  			},
   627  		},
   628  	}
   629  	sig := crypto.SignHash(txn.SigHash(0, s.height), contract.SecretKey)
   630  	txn.TransactionSignatures[0].Signature = sig[:]
   631  
   632  	// fill in the missing request fields
   633  	req.NewRevisionNumber = rev.NewRevisionNumber
   634  	req.NewValidProofValues = make([]types.Currency, len(rev.NewValidProofOutputs))
   635  	for i, o := range rev.NewValidProofOutputs {
   636  		req.NewValidProofValues[i] = o.Value
   637  	}
   638  	req.NewMissedProofValues = make([]types.Currency, len(rev.NewMissedProofOutputs))
   639  	for i, o := range rev.NewMissedProofOutputs {
   640  		req.NewMissedProofValues[i] = o.Value
   641  	}
   642  	req.Signature = sig[:]
   643  
   644  	// record the change we are about to make to the contract. If we lose power
   645  	// mid-revision, this allows us to restore either the pre-revision or
   646  	// post-revision contract.
   647  	walTxn, err := sc.managedRecordDownloadIntent(rev, price)
   648  	if err != nil {
   649  		return modules.RenterContract{}, nil, err
   650  	}
   651  
   652  	// Increase Successful/Failed interactions accordingly
   653  	defer func() {
   654  		if err != nil {
   655  			s.hdb.IncrementFailedInteractions(contract.HostPublicKey())
   656  		} else {
   657  			s.hdb.IncrementSuccessfulInteractions(contract.HostPublicKey())
   658  		}
   659  	}()
   660  
   661  	// send SectorRoots RPC request
   662  	extendDeadline(s.conn, modules.NegotiateDownloadTime)
   663  	var resp modules.LoopSectorRootsResponse
   664  	err = s.call(modules.RPCLoopSectorRoots, req, &resp, modules.RPCMinLen+(req.NumRoots*crypto.HashSize))
   665  	if err != nil {
   666  		return modules.RenterContract{}, nil, err
   667  	}
   668  	// verify the response
   669  	if len(resp.SectorRoots) != int(req.NumRoots) {
   670  		return modules.RenterContract{}, nil, errors.New("host did not send the requested number of sector roots")
   671  	}
   672  	proofStart, proofEnd := int(req.RootOffset), int(req.RootOffset+req.NumRoots)
   673  	if !crypto.VerifySectorRangeProof(resp.SectorRoots, resp.MerkleProof, proofStart, proofEnd, rev.NewFileMerkleRoot) {
   674  		return modules.RenterContract{}, nil, errors.New("host provided incorrect sector data or Merkle proof")
   675  	}
   676  
   677  	// add host signature
   678  	txn.TransactionSignatures[1].Signature = resp.Signature
   679  
   680  	// update contract and metrics
   681  	if err := sc.managedCommitDownload(walTxn, txn, price); err != nil {
   682  		return modules.RenterContract{}, nil, err
   683  	}
   684  
   685  	return sc.Metadata(), resp.SectorRoots, nil
   686  }
   687  
   688  // RecoverSectorRoots calls the contract roots download RPC and returns the requested sector roots. The
   689  // Revision and Signature fields of req are filled in automatically. If a
   690  // Merkle proof is requested, it is verified.
   691  func (s *Session) RecoverSectorRoots(lastRev types.FileContractRevision, sk crypto.SecretKey) (_ types.Transaction, _ []crypto.Hash, err error) {
   692  	// Calculate total roots we need to fetch.
   693  	numRoots := lastRev.NewFileSize / modules.SectorSize
   694  	if lastRev.NewFileSize%modules.SectorSize != 0 {
   695  		numRoots++
   696  	}
   697  	// Create the request.
   698  	req := modules.LoopSectorRootsRequest{
   699  		RootOffset: 0,
   700  		NumRoots:   numRoots,
   701  	}
   702  	// Reset deadline when finished.
   703  	defer extendDeadline(s.conn, time.Hour)
   704  
   705  	// calculate price
   706  	estProofHashes := bits.Len64(lastRev.NewFileSize / modules.SectorSize)
   707  	estBandwidth := (uint64(estProofHashes) + req.NumRoots) * crypto.HashSize
   708  	if estBandwidth < modules.RPCMinLen {
   709  		estBandwidth = modules.RPCMinLen
   710  	}
   711  	bandwidthPrice := s.host.DownloadBandwidthPrice.Mul64(estBandwidth)
   712  	price := s.host.BaseRPCPrice.Add(bandwidthPrice)
   713  	if lastRev.RenterFunds().Cmp(price) < 0 {
   714  		return types.Transaction{}, nil, errors.New("contract has insufficient funds to support sector roots download")
   715  	}
   716  	// To mitigate small errors (e.g. differing block heights), fudge the
   717  	// price and collateral by 0.2%.
   718  	price = price.MulFloat(1 + hostPriceLeeway)
   719  
   720  	// create the download revision and sign it
   721  	rev := newDownloadRevision(lastRev, price)
   722  	txn := types.Transaction{
   723  		FileContractRevisions: []types.FileContractRevision{rev},
   724  		TransactionSignatures: []types.TransactionSignature{
   725  			{
   726  				ParentID:       crypto.Hash(rev.ParentID),
   727  				CoveredFields:  types.CoveredFields{FileContractRevisions: []uint64{0}},
   728  				PublicKeyIndex: 0, // renter key is always first -- see formContract
   729  			},
   730  			{
   731  				ParentID:       crypto.Hash(rev.ParentID),
   732  				PublicKeyIndex: 1,
   733  				CoveredFields:  types.CoveredFields{FileContractRevisions: []uint64{0}},
   734  				Signature:      nil, // to be provided by host
   735  			},
   736  		},
   737  	}
   738  	sig := crypto.SignHash(txn.SigHash(0, s.height), sk)
   739  	txn.TransactionSignatures[0].Signature = sig[:]
   740  
   741  	// fill in the missing request fields
   742  	req.NewRevisionNumber = rev.NewRevisionNumber
   743  	req.NewValidProofValues = make([]types.Currency, len(rev.NewValidProofOutputs))
   744  	for i, o := range rev.NewValidProofOutputs {
   745  		req.NewValidProofValues[i] = o.Value
   746  	}
   747  	req.NewMissedProofValues = make([]types.Currency, len(rev.NewMissedProofOutputs))
   748  	for i, o := range rev.NewMissedProofOutputs {
   749  		req.NewMissedProofValues[i] = o.Value
   750  	}
   751  	req.Signature = sig[:]
   752  
   753  	// Increase Successful/Failed interactions accordingly
   754  	defer func() {
   755  		if err != nil {
   756  			s.hdb.IncrementFailedInteractions(s.host.PublicKey)
   757  		} else {
   758  			s.hdb.IncrementSuccessfulInteractions(s.host.PublicKey)
   759  		}
   760  	}()
   761  
   762  	// send SectorRoots RPC request
   763  	extendDeadline(s.conn, modules.NegotiateDownloadTime)
   764  	var resp modules.LoopSectorRootsResponse
   765  	err = s.call(modules.RPCLoopSectorRoots, req, &resp, modules.RPCMinLen+(req.NumRoots*crypto.HashSize))
   766  	if err != nil {
   767  		return types.Transaction{}, nil, err
   768  	}
   769  	// verify the response
   770  	if len(resp.SectorRoots) != int(req.NumRoots) {
   771  		return types.Transaction{}, nil, errors.New("host did not send the requested number of sector roots")
   772  	}
   773  	proofStart, proofEnd := int(req.RootOffset), int(req.RootOffset+req.NumRoots)
   774  	if !crypto.VerifySectorRangeProof(resp.SectorRoots, resp.MerkleProof, proofStart, proofEnd, rev.NewFileMerkleRoot) {
   775  		return types.Transaction{}, nil, errors.New("host provided incorrect sector data or Merkle proof")
   776  	}
   777  
   778  	// add host signature
   779  	txn.TransactionSignatures[1].Signature = resp.Signature
   780  	return txn, resp.SectorRoots, nil
   781  }
   782  
   783  // shutdown terminates the revision loop and signals the goroutine spawned in
   784  // NewSession to return.
   785  func (s *Session) shutdown() {
   786  	extendDeadline(s.conn, modules.NegotiateSettingsTime)
   787  	// don't care about this error
   788  	_ = s.writeRequest(modules.RPCLoopExit, nil)
   789  	close(s.closeChan)
   790  }
   791  
   792  // Close cleanly terminates the protocol session with the host and closes the
   793  // connection.
   794  func (s *Session) Close() error {
   795  	// using once ensures that Close is idempotent
   796  	s.once.Do(s.shutdown)
   797  	return s.conn.Close()
   798  }
   799  
   800  // NewSession initiates the RPC loop with a host and returns a Session.
   801  func (cs *ContractSet) NewSession(host modules.HostDBEntry, id types.FileContractID, currentHeight types.BlockHeight, hdb hostDB, cancel <-chan struct{}) (_ *Session, err error) {
   802  	sc, ok := cs.Acquire(id)
   803  	if !ok {
   804  		return nil, errors.New("invalid contract")
   805  	}
   806  	defer cs.Return(sc)
   807  	s, err := cs.managedNewSession(host, currentHeight, hdb, cancel)
   808  	if err != nil {
   809  		return nil, err
   810  	}
   811  	// Lock the contract and resynchronize if necessary
   812  	rev, sigs, err := s.Lock(id, sc.header.SecretKey)
   813  	if err != nil {
   814  		s.Close()
   815  		return nil, err
   816  	} else if err := sc.managedSyncRevision(rev, sigs); err != nil {
   817  		s.Close()
   818  		return nil, err
   819  	}
   820  	return s, nil
   821  }
   822  
   823  // NewRawSession creates a new session unassociated with any contract.
   824  func (cs *ContractSet) NewRawSession(host modules.HostDBEntry, currentHeight types.BlockHeight, hdb hostDB, cancel <-chan struct{}) (_ *Session, err error) {
   825  	return cs.managedNewSession(host, currentHeight, hdb, cancel)
   826  }
   827  
   828  // managedNewSession initiates the RPC loop with a host and returns a Session.
   829  func (cs *ContractSet) managedNewSession(host modules.HostDBEntry, currentHeight types.BlockHeight, hdb hostDB, cancel <-chan struct{}) (_ *Session, err error) {
   830  	// Increase Successful/Failed interactions accordingly
   831  	defer func() {
   832  		if err != nil {
   833  			hdb.IncrementFailedInteractions(host.PublicKey)
   834  			err = errors.Extend(err, modules.ErrHostFault)
   835  		} else {
   836  			hdb.IncrementSuccessfulInteractions(host.PublicKey)
   837  		}
   838  	}()
   839  
   840  	c, err := (&net.Dialer{
   841  		Cancel:  cancel,
   842  		Timeout: 45 * time.Second, // TODO: Constant
   843  	}).Dial("tcp", string(host.NetAddress))
   844  	if err != nil {
   845  		return nil, err
   846  	}
   847  	conn := ratelimit.NewRLConn(c, cs.rl, cancel)
   848  
   849  	closeChan := make(chan struct{})
   850  	go func() {
   851  		select {
   852  		case <-cancel:
   853  			conn.Close()
   854  		case <-closeChan:
   855  			// we don't close the connection here because we want session.Close
   856  			// to be able to return the Close error directly
   857  		}
   858  	}()
   859  
   860  	// Perform the handshake and create the session object.
   861  	aead, challenge, err := performSessionHandshake(conn, host.PublicKey)
   862  	if err != nil {
   863  		conn.Close()
   864  		close(closeChan)
   865  		return nil, errors.AddContext(err, "session handshake failed")
   866  	}
   867  	s := &Session{
   868  		aead:        aead,
   869  		challenge:   challenge.Challenge,
   870  		closeChan:   closeChan,
   871  		conn:        conn,
   872  		contractSet: cs,
   873  		deps:        cs.deps,
   874  		hdb:         hdb,
   875  		height:      currentHeight,
   876  		host:        host,
   877  	}
   878  
   879  	return s, nil
   880  }
   881  
   882  // calculateProofRanges returns the proof ranges that should be used to verify a
   883  // pre-modification Merkle diff proof for the specified actions.
   884  func calculateProofRanges(actions []modules.LoopWriteAction, oldNumSectors uint64) []crypto.ProofRange {
   885  	newNumSectors := oldNumSectors
   886  	sectorsChanged := make(map[uint64]struct{})
   887  	for _, action := range actions {
   888  		switch action.Type {
   889  		case modules.WriteActionAppend:
   890  			sectorsChanged[newNumSectors] = struct{}{}
   891  			newNumSectors++
   892  
   893  		case modules.WriteActionTrim:
   894  			newNumSectors--
   895  			sectorsChanged[newNumSectors] = struct{}{}
   896  
   897  		case modules.WriteActionSwap:
   898  			sectorsChanged[action.A] = struct{}{}
   899  			sectorsChanged[action.B] = struct{}{}
   900  
   901  		case modules.WriteActionUpdate:
   902  			panic("update not supported")
   903  		}
   904  	}
   905  
   906  	oldRanges := make([]crypto.ProofRange, 0, len(sectorsChanged))
   907  	for index := range sectorsChanged {
   908  		if index < oldNumSectors {
   909  			oldRanges = append(oldRanges, crypto.ProofRange{
   910  				Start: index,
   911  				End:   index + 1,
   912  			})
   913  		}
   914  	}
   915  	sort.Slice(oldRanges, func(i, j int) bool {
   916  		return oldRanges[i].Start < oldRanges[j].Start
   917  	})
   918  
   919  	return oldRanges
   920  }
   921  
   922  // modifyProofRanges modifies the proof ranges produced by calculateProofRanges
   923  // to verify a post-modification Merkle diff proof for the specified actions.
   924  func modifyProofRanges(proofRanges []crypto.ProofRange, actions []modules.LoopWriteAction, numSectors uint64) []crypto.ProofRange {
   925  	for _, action := range actions {
   926  		switch action.Type {
   927  		case modules.WriteActionAppend:
   928  			proofRanges = append(proofRanges, crypto.ProofRange{
   929  				Start: numSectors,
   930  				End:   numSectors + 1,
   931  			})
   932  			numSectors++
   933  
   934  		case modules.WriteActionTrim:
   935  			proofRanges = proofRanges[:uint64(len(proofRanges))-action.A]
   936  			numSectors--
   937  		}
   938  	}
   939  	return proofRanges
   940  }
   941  
   942  // modifyLeaves modifies the leaf hashes of a Merkle diff proof to verify a
   943  // post-modification Merkle diff proof for the specified actions.
   944  func modifyLeaves(leafHashes []crypto.Hash, actions []modules.LoopWriteAction, numSectors uint64) []crypto.Hash {
   945  	// determine which sector index corresponds to each leaf hash
   946  	var indices []uint64
   947  	for _, action := range actions {
   948  		switch action.Type {
   949  		case modules.WriteActionAppend:
   950  			indices = append(indices, numSectors)
   951  			numSectors++
   952  		case modules.WriteActionTrim:
   953  			for j := uint64(0); j < action.A; j++ {
   954  				indices = append(indices, numSectors)
   955  				numSectors--
   956  			}
   957  		case modules.WriteActionSwap:
   958  			indices = append(indices, action.A, action.B)
   959  		}
   960  	}
   961  	sort.Slice(indices, func(i, j int) bool {
   962  		return indices[i] < indices[j]
   963  	})
   964  	indexMap := make(map[uint64]int, len(leafHashes))
   965  	for i, index := range indices {
   966  		if i > 0 && index == indices[i-1] {
   967  			continue // remove duplicates
   968  		}
   969  		indexMap[index] = i
   970  	}
   971  
   972  	for _, action := range actions {
   973  		switch action.Type {
   974  		case modules.WriteActionAppend:
   975  			leafHashes = append(leafHashes, crypto.MerkleRoot(action.Data))
   976  
   977  		case modules.WriteActionTrim:
   978  			leafHashes = leafHashes[:uint64(len(leafHashes))-action.A]
   979  
   980  		case modules.WriteActionSwap:
   981  			i, j := indexMap[action.A], indexMap[action.B]
   982  			leafHashes[i], leafHashes[j] = leafHashes[j], leafHashes[i]
   983  
   984  		case modules.WriteActionUpdate:
   985  			panic("update not supported")
   986  		}
   987  	}
   988  	return leafHashes
   989  }