func verifyPaymentRevision(existingRevision, paymentRevision types.FileContractRevision, blockHeight types.BlockHeight, expectedTransfer types.Currency) error {
	if len(paymentRevision.NewValidProofOutputs) != 2 || len(paymentRevision.NewMissedProofOutputs) != 3 {
		return errBadContractOutputCounts
	}
	if existingRevision.NewWindowStart-revisionSubmissionBuffer <= blockHeight {
		return errLateRevision
	}
	if paymentRevision.NewValidProofOutputs[1].UnlockHash != existingRevision.NewValidProofOutputs[1].UnlockHash {
		return errors.New("host payout address changed")
	}
	if paymentRevision.NewMissedProofOutputs[1].UnlockHash != existingRevision.NewMissedProofOutputs[1].UnlockHash {
		return errors.New("host payout address changed")
	}
	if paymentRevision.NewMissedProofOutputs[2].UnlockHash != existingRevision.NewMissedProofOutputs[2].UnlockHash {
		return errors.New("lost collateral address was changed")
	}
	if paymentRevision.NewValidProofOutputs[0].Value.Cmp(existingRevision.NewValidProofOutputs[0].Value) > 0 {
		return extendErr("renter increased its valid proof output: ", errHighRenterValidOutput)
	}
	fromRenter := existingRevision.NewValidProofOutputs[0].Value.Sub(paymentRevision.NewValidProofOutputs[0].Value)
	if fromRenter.Cmp(expectedTransfer) < 0 {
		s := fmt.Sprintf("expected at least %v to be exchanged, but %v was exchanged: ", expectedTransfer, fromRenter)
		return extendErr(s, errHighRenterValidOutput)
	}
	if existingRevision.NewValidProofOutputs[1].Value.Cmp(paymentRevision.NewValidProofOutputs[1].Value) > 0 {
		return extendErr("host valid proof output was decreased: ", errLowHostValidOutput)
	}
	toHost := paymentRevision.NewValidProofOutputs[1].Value.Sub(existingRevision.NewValidProofOutputs[1].Value)
	if !toHost.Equals(fromRenter) {
		s := fmt.Sprintf("expected exactly %v to be transferred to the host, but %v was transferred: ", fromRenter, toHost)
		return extendErr(s, errLowHostValidOutput)
	}
	if paymentRevision.NewValidProofOutputs[0].Value.Cmp(paymentRevision.NewMissedProofOutputs[0].Value) > 0 {
		return extendErr("renter has incentive to see host fail: ", errHighRenterMissedOutput)
	}
	if paymentRevision.NewMissedProofOutputs[1].Value.Cmp(existingRevision.NewMissedProofOutputs[1].Value) < 0 {
		collateral := existingRevision.NewMissedProofOutputs[1].Value.Sub(paymentRevision.NewMissedProofOutputs[1].Value)
		s := fmt.Sprintf("host not expecting to post any collateral, but contract has host posting %v collateral: ", collateral)
		return extendErr(s, errLowHostMissedOutput)
	}
	if paymentRevision.NewRevisionNumber <= existingRevision.NewRevisionNumber {
		return errBadRevisionNumber
	}
	if paymentRevision.ParentID != existingRevision.ParentID {
		return errBadParentID
	}
	if paymentRevision.UnlockConditions.UnlockHash() != existingRevision.UnlockConditions.UnlockHash() {
		return errBadUnlockConditions
	}
	if paymentRevision.NewFileSize != existingRevision.NewFileSize {
		return errBadFileSize
	}
	if paymentRevision.NewFileMerkleRoot != existingRevision.NewFileMerkleRoot {
		return errBadFileMerkleRoot
	}
	if paymentRevision.NewWindowStart != existingRevision.NewWindowStart {
		return errBadWindowStart
	}
	if paymentRevision.NewWindowEnd != existingRevision.NewWindowEnd {
		return errBadWindowEnd
	}
	if paymentRevision.NewUnlockHash != existingRevision.NewUnlockHash {
		return errBadUnlockHash
	}
	if !paymentRevision.NewMissedProofOutputs[1].Value.Equals(existingRevision.NewMissedProofOutputs[1].Value) {
		return errLowHostMissedOutput
	}
	return nil
}
verifyPaymentRevision verifies that the revision being provided to pay for
the data has transferred the expected amount of money from the renter to the
host.
package host
import (
	"errors"
	"fmt"
	"net"
	"time"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)
var (
	errLargeDownloadBatch = ErrorCommunication("download request exceeded maximum batch size")
	errRequestOutOfBounds = ErrorCommunication("download request has invalid sector bounds")
)
func (h *Host) managedDownloadIteration(conn net.Conn, so *storageObligation) error {
	err := h.managedRPCSettings(conn)
	if err != nil {
		return extendErr("RPCSettings failed: ", err)
	}
	conn.SetDeadline(time.Now().Add(modules.NegotiateDownloadTime))
	err = modules.ReadNegotiationAcceptance(conn)
	if err == modules.ErrStopResponse {
		return err  
	} else if err != nil {
		return extendErr("renter rejected host settings: ", ErrorCommunication(err.Error()))
	}
	h.mu.Lock()
	blockHeight := h.blockHeight
	secretKey := h.secretKey
	settings := h.externalSettings()
	h.mu.Unlock()
	var requests []modules.DownloadAction
	var paymentRevision types.FileContractRevision
	err = encoding.ReadObject(conn, &requests, modules.NegotiateMaxDownloadActionRequestSize)
	if err != nil {
		return extendErr("failed to read download requests:", ErrorConnection(err.Error()))
	}
	err = encoding.ReadObject(conn, &paymentRevision, modules.NegotiateMaxFileContractRevisionSize)
	if err != nil {
		return extendErr("failed to read payment revision:", ErrorConnection(err.Error()))
	}
	existingRevision := so.RevisionTransactionSet[len(so.RevisionTransactionSet)-1].FileContractRevisions[0]
	var payload [][]byte
	err = func() error {
		var totalSize uint64
		for _, request := range requests {
			if request.Length > modules.SectorSize || request.Offset+request.Length > modules.SectorSize {
				return extendErr("download iteration request failed: ", errRequestOutOfBounds)
			}
			totalSize += request.Length
		}
		if totalSize > settings.MaxDownloadBatchSize {
			return extendErr("download iteration batch failed: ", errLargeDownloadBatch)
		}
		expectedTransfer := settings.DownloadBandwidthPrice.Mul64(totalSize)
		err = verifyPaymentRevision(existingRevision, paymentRevision, blockHeight, expectedTransfer)
		if err != nil {
			return extendErr("payment verification failed: ", err)
		}
		for _, request := range requests {
			sectorData, err := h.ReadSector(request.MerkleRoot)
			if err != nil {
				return extendErr("failed to load sector: ", ErrorInternal(err.Error()))
			}
			payload = append(payload, sectorData[request.Offset:request.Offset+request.Length])
		}
		return nil
	}()
	if err != nil {
		modules.WriteNegotiationRejection(conn, err)  
		return extendErr("download request rejected: ", err)
	}
	err = modules.WriteNegotiationAcceptance(conn)
	if err != nil {
		return extendErr("failed to write acceptance for renter revision: ", ErrorConnection(err.Error()))
	}
	var renterSignature types.TransactionSignature
	err = encoding.ReadObject(conn, &renterSignature, modules.NegotiateMaxTransactionSignatureSize)
	if err != nil {
		return extendErr("failed to read renter signature: ", ErrorConnection(err.Error()))
	}
	txn, err := createRevisionSignature(paymentRevision, renterSignature, secretKey, blockHeight)
	paymentTransfer := existingRevision.NewValidProofOutputs[0].Value.Sub(paymentRevision.NewValidProofOutputs[0].Value)
	so.PotentialDownloadRevenue = so.PotentialDownloadRevenue.Add(paymentTransfer)
	so.RevisionTransactionSet = []types.Transaction{{
		FileContractRevisions: []types.FileContractRevision{paymentRevision},
		TransactionSignatures: []types.TransactionSignature{renterSignature, txn.TransactionSignatures[1]},
	}}
	h.mu.Lock()
	err = h.modifyStorageObligation(*so, nil, nil, nil)
	h.mu.Unlock()
	if err != nil {
		return extendErr("failed to modify storage obligation: ", ErrorInternal(modules.WriteNegotiationRejection(conn, err).Error()))
	}
	err = modules.WriteNegotiationAcceptance(conn)
	if err != nil {
		return extendErr("failed to write acceptance following obligation modification: ", ErrorConnection(err.Error()))
	}
	err = encoding.WriteObject(conn, txn.TransactionSignatures[1])
	if err != nil {
		return extendErr("failed to write signature: ", ErrorConnection(err.Error()))
	}
	err = encoding.WriteObject(conn, payload)
	if err != nil {
		return extendErr("failed to write payload: ", ErrorConnection(err.Error()))
	}
	return nil
}