gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/proto/renew.go (about)

     1  package proto
     2  
     3  import (
     4  	"math"
     5  	"net"
     6  
     7  	"gitlab.com/NebulousLabs/errors"
     8  
     9  	"gitlab.com/SkynetLabs/skyd/build"
    10  	"gitlab.com/SkynetLabs/skyd/skymodules"
    11  	"go.sia.tech/siad/crypto"
    12  	"go.sia.tech/siad/modules"
    13  	"go.sia.tech/siad/types"
    14  	"go.sia.tech/siad/types/typesutil"
    15  )
    16  
    17  // FileContractTxnEstimateMultiplier is a multiplier for the estimation of the
    18  // size of a renew txn including a file contract and revision.
    19  const FileContractTxnEstimateMultiplier = 3
    20  
    21  // Renew negotiates a new contract for data already stored with a host, and
    22  // submits the new contract transaction to tpool. The new contract is added to
    23  // the ContractSet and its metadata is returned.
    24  func (cs *ContractSet) Renew(oldContract *SafeContract, params skymodules.ContractParams, txnBuilder transactionBuilder, tpool transactionPool, hdb hostDB, cancel <-chan struct{}) (rc skymodules.RenterContract, formationTxnSet []types.Transaction, err error) {
    25  	// Check that the host version is high enough as belt-and-suspenders. This
    26  	// should never happen, because hosts with old versions should be blacklisted
    27  	// by the contractor.
    28  	if build.VersionCmp(params.Host.Version, "1.4.4") < 0 {
    29  		return skymodules.RenterContract{}, nil, ErrBadHostVersion
    30  	}
    31  	return cs.managedNewRenewAndClear(oldContract, params, txnBuilder, tpool, hdb, cancel)
    32  }
    33  
    34  // managedNewRenewAndClear uses the new RPC to renew a contract, creating a new
    35  // contract that is identical to the old one, and then clears the old one to be
    36  // empty.
    37  func (cs *ContractSet) managedNewRenewAndClear(oldContract *SafeContract, params skymodules.ContractParams, txnBuilder transactionBuilder, tpool transactionPool, hdb hostDB, cancel <-chan struct{}) (rc skymodules.RenterContract, formationTxnSet []types.Transaction, err error) {
    38  	// for convenience
    39  	contract := oldContract.header
    40  
    41  	// Extract vars from params, for convenience.
    42  	fcTxn, _ := txnBuilder.View()
    43  	host, funding, startHeight, endHeight := params.Host, params.Funding, params.StartHeight, params.EndHeight
    44  	ourSKOld := contract.SecretKey
    45  	ourSKNew, ourPKNew := skymodules.GenerateContractKeyPair(params.RenterSeed, fcTxn)
    46  	lastRev := contract.LastRevision()
    47  
    48  	// Calculate the anticipated transaction fee.
    49  	_, maxFee := tpool.FeeEstimation()
    50  	txnFee := maxFee.Mul64(skymodules.EstimatedFileContractTransactionSetSize)
    51  
    52  	// Calculate the base cost.
    53  	basePrice, baseCollateral := rhp2BaseCosts(lastRev, host, endHeight)
    54  
    55  	// Create file contract and add it together with the fee to the builder.
    56  	uc := createFileContractUnlockConds(host.PublicKey, ourPKNew)
    57  	uh := uc.UnlockHash()
    58  	fc, err := createRenewedContract(lastRev, uh, params, txnFee, basePrice, baseCollateral, tpool)
    59  	if err != nil {
    60  		return skymodules.RenterContract{}, nil, err
    61  	}
    62  	txnBuilder.AddFileContract(fc)
    63  	txnBuilder.AddMinerFee(txnFee)
    64  
    65  	// Add FileContract identifier.
    66  	si, hk := skymodules.PrefixedSignedIdentifier(params.RenterSeed, fcTxn, host.PublicKey)
    67  	_ = txnBuilder.AddArbitraryData(append(si[:], hk[:]...))
    68  
    69  	// Create initial transaction set.
    70  	txnSet, err := prepareTransactionSet(txnBuilder)
    71  	if err != nil {
    72  		return skymodules.RenterContract{}, nil, err
    73  	}
    74  
    75  	// Increase Successful/Failed interactions accordingly
    76  	defer func() {
    77  		// A revision mismatch might not be the host's fault.
    78  		if err != nil && !IsRevisionMismatch(err) {
    79  			hdb.IncrementFailedInteractions(contract.HostPublicKey())
    80  			err = errors.Extend(err, skymodules.ErrHostFault)
    81  		} else if err == nil {
    82  			hdb.IncrementSuccessfulInteractions(contract.HostPublicKey())
    83  		}
    84  	}()
    85  
    86  	// Initiate protocol.
    87  	s, err := cs.NewRawSession(host, startHeight, hdb, cancel)
    88  	if err != nil {
    89  		return skymodules.RenterContract{}, nil, err
    90  	}
    91  	defer func() {
    92  		err = errors.Compose(err, s.Close())
    93  	}()
    94  
    95  	// Lock the contract and resynchronize if necessary
    96  	rev, sigs, err := s.Lock(contract.ID(), ourSKOld)
    97  	if err != nil {
    98  		return skymodules.RenterContract{}, nil, err
    99  	} else if err := oldContract.managedSyncRevision(rev, sigs); err != nil {
   100  		return skymodules.RenterContract{}, nil, err
   101  	}
   102  
   103  	// Create the final revision of the old contract.
   104  	bandwidthCost := host.BaseRPCPrice
   105  	finalRev, err := prepareFinalRevision(contract, bandwidthCost)
   106  	if err != nil {
   107  		return skymodules.RenterContract{}, nil, errors.AddContext(err, "Unable to create final revision")
   108  	}
   109  
   110  	// Create the RenewContract request.
   111  	req := modules.LoopRenewAndClearContractRequest{
   112  		Transactions: txnSet,
   113  		RenterKey:    types.Ed25519PublicKey(ourPKNew),
   114  	}
   115  	for _, vpo := range finalRev.NewValidProofOutputs {
   116  		req.FinalValidProofValues = append(req.FinalValidProofValues, vpo.Value)
   117  	}
   118  	for _, mpo := range finalRev.NewMissedProofOutputs {
   119  		req.FinalMissedProofValues = append(req.FinalMissedProofValues, mpo.Value)
   120  	}
   121  
   122  	// Send the request.
   123  	if err := s.writeRequest(modules.RPCLoopRenewClearContract, req); err != nil {
   124  		return skymodules.RenterContract{}, nil, err
   125  	}
   126  
   127  	// Record the changes we are about to make to the contract.
   128  	walTxn, err := oldContract.managedRecordClearContractIntent(finalRev, bandwidthCost)
   129  	if err != nil {
   130  		return skymodules.RenterContract{}, nil, err
   131  	}
   132  
   133  	// Read the host's response.
   134  	var resp modules.LoopContractAdditions
   135  	if err := s.readResponse(&resp, modules.RPCMinLen); err != nil {
   136  		return skymodules.RenterContract{}, nil, err
   137  	}
   138  
   139  	// Incorporate host's modifications.
   140  	txnBuilder.AddParents(resp.Parents)
   141  	for _, input := range resp.Inputs {
   142  		txnBuilder.AddSiacoinInput(input)
   143  	}
   144  	for _, output := range resp.Outputs {
   145  		txnBuilder.AddSiacoinOutput(output)
   146  	}
   147  
   148  	// sign the final revision of the old contract.
   149  	rev.NewFileMerkleRoot = crypto.Hash{}
   150  	finalRevTxn := types.Transaction{
   151  		FileContractRevisions: []types.FileContractRevision{finalRev},
   152  		TransactionSignatures: []types.TransactionSignature{
   153  			{
   154  				ParentID:       crypto.Hash(finalRev.ParentID),
   155  				CoveredFields:  types.CoveredFields{FileContractRevisions: []uint64{0}},
   156  				PublicKeyIndex: 0, // renter key is always first -- see formContract
   157  			},
   158  			{
   159  				ParentID:       crypto.Hash(finalRev.ParentID),
   160  				PublicKeyIndex: 1,
   161  				CoveredFields:  types.CoveredFields{FileContractRevisions: []uint64{0}},
   162  				Signature:      nil, // to be provided by host
   163  			},
   164  		},
   165  	}
   166  	finalRevSig := crypto.SignHash(finalRevTxn.SigHash(0, s.height), ourSKOld)
   167  	finalRevTxn.TransactionSignatures[0].Signature = finalRevSig[:]
   168  
   169  	// sign the txn
   170  	signedTxnSet, err := txnBuilder.Sign(true)
   171  	if err != nil {
   172  		err = errors.New("failed to sign transaction: " + err.Error())
   173  		modules.WriteRPCResponse(s.conn, s.aead, nil, err)
   174  		return skymodules.RenterContract{}, nil, err
   175  	}
   176  
   177  	// calculate signatures added by the transaction builder
   178  	var addedSignatures []types.TransactionSignature
   179  	_, _, _, addedSignatureIndices := txnBuilder.ViewAdded()
   180  	for _, i := range addedSignatureIndices {
   181  		addedSignatures = append(addedSignatures, signedTxnSet[len(signedTxnSet)-1].TransactionSignatures[i])
   182  	}
   183  
   184  	// create initial (no-op) revision, transaction, and signature
   185  	revisionTxn := prepareInitRevisionTxn(lastRev, uc, fc, startHeight, ourSKNew, signedTxnSet[len(signedTxnSet)-1].FileContractID(0))
   186  
   187  	// Send acceptance and signatures
   188  	renterSigs := modules.LoopRenewAndClearContractSignatures{
   189  		ContractSignatures: addedSignatures,
   190  		RevisionSignature:  revisionTxn.TransactionSignatures[0],
   191  
   192  		FinalRevisionSignature: finalRevSig[:],
   193  	}
   194  	if err := modules.WriteRPCResponse(s.conn, s.aead, renterSigs, nil); err != nil {
   195  		return skymodules.RenterContract{}, nil, err
   196  	}
   197  
   198  	// Read the host acceptance and signatures.
   199  	var hostSigs modules.LoopRenewAndClearContractSignatures
   200  	if err := s.readResponse(&hostSigs, modules.RPCMinLen); err != nil {
   201  		return skymodules.RenterContract{}, nil, err
   202  	}
   203  	for _, sig := range hostSigs.ContractSignatures {
   204  		txnBuilder.AddTransactionSignature(sig)
   205  	}
   206  	revisionTxn.TransactionSignatures = append(revisionTxn.TransactionSignatures, hostSigs.RevisionSignature)
   207  	finalRevTxn.TransactionSignatures[1].Signature = hostSigs.FinalRevisionSignature
   208  
   209  	// Construct the final transaction.
   210  	txnSet, err = prepareTransactionSet(txnBuilder)
   211  	if err != nil {
   212  		return skymodules.RenterContract{}, nil, err
   213  	}
   214  
   215  	// Submit to blockchain.
   216  	err = tpool.AcceptTransactionSet(txnSet)
   217  	if errors.Contains(err, modules.ErrDuplicateTransactionSet) {
   218  		// As long as it made it into the transaction pool, we're good.
   219  		err = nil
   220  	}
   221  	if err != nil {
   222  		return skymodules.RenterContract{}, nil, err
   223  	}
   224  	err = tpool.AcceptTransactionSet([]types.Transaction{finalRevTxn})
   225  	if errors.Contains(err, modules.ErrDuplicateTransactionSet) {
   226  		// As long as it made it into the transaction pool, we're good.
   227  		err = nil
   228  	}
   229  	if err != nil {
   230  		return skymodules.RenterContract{}, nil, err
   231  	}
   232  
   233  	// Construct contract header.
   234  	header := contractHeader{
   235  		Transaction:     revisionTxn,
   236  		SecretKey:       ourSKNew,
   237  		StartHeight:     startHeight,
   238  		TotalCost:       funding,
   239  		ContractFee:     host.ContractPrice,
   240  		TxnFee:          txnFee,
   241  		SiafundFee:      types.Tax(startHeight, fc.Payout),
   242  		StorageSpending: basePrice,
   243  		Utility: skymodules.ContractUtility{
   244  			GoodForUpload:  true,
   245  			GoodForRefresh: true,
   246  			GoodForRenew:   true,
   247  		},
   248  	}
   249  
   250  	// Get old roots
   251  	oldRoots, err := oldContract.merkleRoots.merkleRoots()
   252  	if err != nil {
   253  		return skymodules.RenterContract{}, nil, err
   254  	}
   255  
   256  	// Add contract to set.
   257  	meta, err := cs.managedInsertContract(header, oldRoots)
   258  	if err != nil {
   259  		return skymodules.RenterContract{}, nil, err
   260  	}
   261  	// Commit changes to old contract.
   262  	if err := oldContract.managedCommitClearContract(walTxn, finalRevTxn, bandwidthCost); err != nil {
   263  		return skymodules.RenterContract{}, nil, err
   264  	}
   265  	return meta, txnSet, nil
   266  }
   267  
   268  // rhp2BaseCosts computes the base costs for renewing a contract.
   269  func rhp2BaseCosts(lastRev types.FileContractRevision, host skymodules.HostDBEntry, endHeight types.BlockHeight) (basePrice, baseCollateral types.Currency) {
   270  	// If the contract height did not increase, basePrice and baseCollateral are
   271  	// zero.
   272  	if endHeight+host.WindowSize > lastRev.NewWindowEnd {
   273  		timeExtension := uint64((endHeight + host.WindowSize) - lastRev.NewWindowEnd)
   274  		basePrice = host.StoragePrice.Mul64(lastRev.NewFileSize).Mul64(timeExtension)    // cost of already uploaded data that needs to be covered by the renewed contract.
   275  		baseCollateral = host.Collateral.Mul64(lastRev.NewFileSize).Mul64(timeExtension) // same as basePrice.
   276  	}
   277  	return
   278  }
   279  
   280  // prepareInitRevisionTxn creates the initRevision, places it in a transaction and
   281  // adds the signature or the revision to the transaction.
   282  func prepareInitRevisionTxn(lastRev types.FileContractRevision, uc types.UnlockConditions, newContract types.FileContract, startHeight types.BlockHeight, renterSK crypto.SecretKey, parentID types.FileContractID) types.Transaction {
   283  	initRevision := types.FileContractRevision{
   284  		ParentID:          parentID,
   285  		UnlockConditions:  uc,
   286  		NewRevisionNumber: 1,
   287  
   288  		NewFileSize:           newContract.FileSize,
   289  		NewFileMerkleRoot:     newContract.FileMerkleRoot,
   290  		NewWindowStart:        newContract.WindowStart,
   291  		NewWindowEnd:          newContract.WindowEnd,
   292  		NewValidProofOutputs:  newContract.ValidProofOutputs,
   293  		NewMissedProofOutputs: newContract.MissedProofOutputs,
   294  		NewUnlockHash:         newContract.UnlockHash,
   295  	}
   296  
   297  	renterRevisionSig := types.TransactionSignature{
   298  		ParentID:       crypto.Hash(initRevision.ParentID),
   299  		PublicKeyIndex: 0,
   300  		CoveredFields: types.CoveredFields{
   301  			FileContractRevisions: []uint64{0},
   302  		},
   303  	}
   304  
   305  	revisionTxn := types.Transaction{
   306  		FileContractRevisions: []types.FileContractRevision{initRevision},
   307  		TransactionSignatures: []types.TransactionSignature{renterRevisionSig},
   308  	}
   309  
   310  	encodedSig := crypto.SignHash(revisionTxn.SigHash(0, startHeight), renterSK)
   311  	revisionTxn.TransactionSignatures[0].Signature = encodedSig[:]
   312  	return revisionTxn
   313  }
   314  
   315  // prepareFinalRevision creates a new revision for a contract which transfers
   316  // the given amount of payment, clears the contract and sets the missed outputs
   317  // to equal the valid outputs.
   318  func prepareFinalRevision(contract contractHeader, payment types.Currency) (types.FileContractRevision, error) {
   319  	finalRev, err := contract.LastRevision().PaymentRevision(payment)
   320  	if err != nil {
   321  		return types.FileContractRevision{}, err
   322  	}
   323  
   324  	// Clear the revision.
   325  	finalRev.NewFileSize = 0
   326  	finalRev.NewFileMerkleRoot = crypto.Hash{}
   327  	finalRev.NewRevisionNumber = math.MaxUint64
   328  
   329  	// The valid proof outputs become the missed ones since the host won't need
   330  	// to provide a storage proof.
   331  	finalRev.NewMissedProofOutputs = finalRev.NewValidProofOutputs
   332  	return finalRev, nil
   333  }
   334  
   335  // PrepareTransactionSet prepares a transaction set from the given builder to be
   336  // sent to the host. It includes all unconfirmed parents and has all
   337  // non-essential txns trimmed from it.
   338  func prepareTransactionSet(txnBuilder transactionBuilder) ([]types.Transaction, error) {
   339  	txn, parentTxns := txnBuilder.View()
   340  	unconfirmedParents, err := txnBuilder.UnconfirmedParents()
   341  	if err != nil {
   342  		return nil, err
   343  	}
   344  	txnSet := append(unconfirmedParents, parentTxns...)
   345  	txnSet = typesutil.MinimumTransactionSet([]types.Transaction{txn}, txnSet)
   346  	return txnSet, nil
   347  }
   348  
   349  // createRenewedContract creates a new contract from another contract's last
   350  // revision given some additional renewal parameters.
   351  func createRenewedContract(lastRev types.FileContractRevision, uh types.UnlockHash, params skymodules.ContractParams, txnFee, basePrice, baseCollateral types.Currency, tpool transactionPool) (types.FileContract, error) {
   352  	allowance, startHeight, endHeight, host, funding := params.Allowance, params.StartHeight, params.EndHeight, params.Host, params.Funding
   353  
   354  	// Calculate the payouts for the renter, host, and whole contract.
   355  	period := endHeight - startHeight
   356  	renterPayout, hostPayout, hostCollateral, err := skymodules.RenterPayoutsPreTax(host, funding, txnFee, basePrice, baseCollateral, period, allowance.ExpectedStorage/allowance.Hosts)
   357  	if err != nil {
   358  		return types.FileContract{}, err
   359  	}
   360  	totalPayout := renterPayout.Add(hostPayout)
   361  
   362  	// check for negative currency
   363  	if hostCollateral.Cmp(baseCollateral) < 0 {
   364  		baseCollateral = hostCollateral
   365  	}
   366  	if types.PostTax(params.StartHeight, totalPayout).Cmp(hostPayout) < 0 {
   367  		return types.FileContract{}, errors.New("insufficient funds to pay both siafund fee and also host payout")
   368  	}
   369  
   370  	fc := types.FileContract{
   371  		FileSize:       lastRev.NewFileSize,
   372  		FileMerkleRoot: lastRev.NewFileMerkleRoot,
   373  		WindowStart:    params.EndHeight,
   374  		WindowEnd:      params.EndHeight + params.Host.WindowSize,
   375  		Payout:         totalPayout,
   376  		UnlockHash:     uh,
   377  		RevisionNumber: 0,
   378  		ValidProofOutputs: []types.SiacoinOutput{
   379  			// renter
   380  			{Value: types.PostTax(params.StartHeight, totalPayout).Sub(hostPayout), UnlockHash: params.RefundAddress},
   381  			// host
   382  			{Value: hostPayout, UnlockHash: params.Host.UnlockHash},
   383  		},
   384  		MissedProofOutputs: []types.SiacoinOutput{
   385  			// renter
   386  			{Value: types.PostTax(params.StartHeight, totalPayout).Sub(hostPayout), UnlockHash: params.RefundAddress},
   387  			// host gets its unused collateral back, plus the contract price
   388  			{Value: hostCollateral.Sub(baseCollateral).Add(params.Host.ContractPrice), UnlockHash: params.Host.UnlockHash},
   389  			// void gets the spent storage fees, plus the collateral being risked
   390  			{Value: basePrice.Add(baseCollateral), UnlockHash: types.UnlockHash{}},
   391  		},
   392  	}
   393  	return fc, nil
   394  }
   395  
   396  // RenewContract takes an established connection to a host and renews the
   397  // contract with that host.
   398  func (cs *ContractSet) RenewContract(conn net.Conn, fcid types.FileContractID, params skymodules.ContractParams, txnBuilder modules.TransactionBuilder, tpool modules.TransactionPool, hdb hostDB, pt *modules.RPCPriceTable) (_ skymodules.RenterContract, _ []types.Transaction, err error) {
   399  	// Fetch the contract.
   400  	oldSC, ok := cs.Acquire(fcid)
   401  	if !ok {
   402  		return skymodules.RenterContract{}, nil, errors.New("RenewContract: failed to acquire contract to renew")
   403  	}
   404  	defer cs.Return(oldSC)
   405  	oldContract := oldSC.header
   406  	oldRev := oldContract.LastRevision()
   407  
   408  	// Extract vars from params, for convenience.
   409  	fcTxn, _ := txnBuilder.View()
   410  	host, funding, startHeight, endHeight := params.Host, params.Funding, params.StartHeight, params.EndHeight
   411  	ourSKOld := oldContract.SecretKey
   412  	ourSKNew, ourPKNew := skymodules.GenerateContractKeyPair(params.RenterSeed, fcTxn)
   413  
   414  	// Pay the host an insufficient amount.
   415  	if cs.staticDeps.Disrupt("DefaultRenewSettings") {
   416  		ptNew := *pt
   417  		ptNew.WriteLengthCost = ptNew.WriteLengthCost.Sub64(1)
   418  		pt = &ptNew
   419  	}
   420  
   421  	// RHP3 contains both the contract and final revision. So we double the
   422  	// estimation.
   423  	txnFee := pt.TxnFeeMaxRecommended.Mul64(FileContractTxnEstimateMultiplier * skymodules.EstimatedFileContractTransactionSetSize)
   424  
   425  	// Calculate the base cost. This includes the RPC cost.
   426  	basePrice, baseCollateral := skymodules.RenewBaseCosts(oldRev, pt, endHeight)
   427  
   428  	// Create the final revision of the old contract.
   429  	renewCost := types.ZeroCurrency
   430  	finalRev, err := prepareFinalRevision(oldContract, renewCost)
   431  	if err != nil {
   432  		return skymodules.RenterContract{}, nil, errors.AddContext(err, "Unable to create final revision")
   433  	}
   434  
   435  	// Record the changes we are about to make to the contract.
   436  	walTxn, err := oldSC.managedRecordClearContractIntent(finalRev, renewCost)
   437  	if err != nil {
   438  		return skymodules.RenterContract{}, nil, errors.AddContext(err, "failed to record clear contract intent")
   439  	}
   440  
   441  	// Create the new file contract.
   442  	uc := createFileContractUnlockConds(host.PublicKey, ourPKNew)
   443  	uh := uc.UnlockHash()
   444  	fc, err := createRenewedContract(oldRev, uh, params, txnFee, basePrice, baseCollateral, tpool)
   445  	if err != nil {
   446  		return skymodules.RenterContract{}, nil, errors.AddContext(err, "Unable to create new contract")
   447  	}
   448  
   449  	// Add both the new final revision and the new contract to the same
   450  	// transaction.
   451  	txnBuilder.AddFileContractRevision(finalRev)
   452  	txnBuilder.AddFileContract(fc)
   453  
   454  	// Add the fee to the transaction.
   455  	txnBuilder.AddMinerFee(txnFee)
   456  
   457  	// Add FileContract identifier.
   458  	si, hk := skymodules.PrefixedSignedIdentifier(params.RenterSeed, fcTxn, host.PublicKey)
   459  	_ = txnBuilder.AddArbitraryData(append(si[:], hk[:]...))
   460  
   461  	// Create transaction set.
   462  	txnSet, err := prepareTransactionSet(txnBuilder)
   463  	if err != nil {
   464  		return skymodules.RenterContract{}, nil, errors.AddContext(err, "failed to prepare txnSet with finalRev and new contract")
   465  	}
   466  
   467  	// Increase Successful/Failed interactions accordingly
   468  	defer func() {
   469  		if err != nil {
   470  			hdb.IncrementFailedInteractions(host.PublicKey)
   471  			err = errors.Compose(err, skymodules.ErrHostFault)
   472  		} else if err == nil {
   473  			hdb.IncrementSuccessfulInteractions(host.PublicKey)
   474  		}
   475  	}()
   476  
   477  	// Sign the final revision.
   478  	finalRevRenterSig := types.TransactionSignature{
   479  		ParentID:       crypto.Hash(finalRev.ParentID),
   480  		PublicKeyIndex: 0, // renter key is first
   481  		CoveredFields: types.CoveredFields{
   482  			FileContracts:         []uint64{0},
   483  			FileContractRevisions: []uint64{0},
   484  		},
   485  	}
   486  	finalRevTxn, _ := txnBuilder.View()
   487  	finalRevTxn.TransactionSignatures = append(finalRevTxn.TransactionSignatures, finalRevRenterSig)
   488  	finalRevRenterSigRaw := crypto.SignHash(finalRevTxn.SigHash(0, pt.HostBlockHeight), ourSKOld)
   489  	finalRevRenterSig.Signature = finalRevRenterSigRaw[:]
   490  	// Write the request.
   491  	err = modules.RPCWrite(conn, modules.RPCRenewContractRequest{
   492  		TSet:        txnSet,
   493  		RenterPK:    types.Ed25519PublicKey(ourPKNew),
   494  		FinalRevSig: finalRevRenterSigRaw,
   495  	})
   496  	if err != nil {
   497  		return skymodules.RenterContract{}, nil, errors.AddContext(err, "failed to write RPCRenewContractRequest")
   498  	}
   499  
   500  	// Read the response. It contains the host's final revision sig and any
   501  	// additions it made.
   502  	var resp modules.RPCRenewContractCollateralResponse
   503  	err = modules.RPCReadMaxLen(conn, &resp, modules.RenewDecodeMaxLen)
   504  	if err != nil {
   505  		return skymodules.RenterContract{}, nil, errors.AddContext(err, "failed to read RPCRenewContractCollateralResponse")
   506  	}
   507  
   508  	// Incorporate host's modifications.
   509  	txnBuilder.AddParents(resp.NewParents)
   510  	for _, input := range resp.NewInputs {
   511  		txnBuilder.AddSiacoinInput(input)
   512  	}
   513  	for _, output := range resp.NewOutputs {
   514  		txnBuilder.AddSiacoinOutput(output)
   515  	}
   516  
   517  	// Create the host sig for the final revision.
   518  	finalRevHostSigRaw := resp.FinalRevSig
   519  	finalRevHostSig := types.TransactionSignature{
   520  		ParentID:       crypto.Hash(finalRev.ParentID),
   521  		PublicKeyIndex: 1,
   522  		CoveredFields: types.CoveredFields{
   523  			FileContracts:         []uint64{0},
   524  			FileContractRevisions: []uint64{0},
   525  		},
   526  		Signature: finalRevHostSigRaw[:],
   527  	}
   528  
   529  	// Add the revision signatures to the transaction set and sign it.
   530  	_ = txnBuilder.AddTransactionSignature(finalRevRenterSig)
   531  	_ = txnBuilder.AddTransactionSignature(finalRevHostSig)
   532  	signedTxnSet, err := txnBuilder.Sign(true)
   533  	if err != nil {
   534  		return skymodules.RenterContract{}, nil, errors.AddContext(err, "failed to sign transaction set")
   535  	}
   536  
   537  	// Calculate signatures added by the transaction builder
   538  	var addedSignatures []types.TransactionSignature
   539  	_, _, _, addedSignatureIndices := txnBuilder.ViewAdded()
   540  	for _, i := range addedSignatureIndices {
   541  		addedSignatures = append(addedSignatures, signedTxnSet[len(signedTxnSet)-1].TransactionSignatures[i])
   542  	}
   543  
   544  	// Create initial (no-op) revision, transaction, and signature
   545  	noOpRevTxn := prepareInitRevisionTxn(oldRev, uc, fc, startHeight, ourSKNew, signedTxnSet[len(signedTxnSet)-1].FileContractID(0))
   546  	// Send transaction signatures and no-op revision signature to host.
   547  	err = modules.RPCWrite(conn, modules.RPCRenewContractRenterSignatures{
   548  		RenterNoOpRevisionSig: noOpRevTxn.RenterSignature(),
   549  		RenterTxnSigs:         addedSignatures,
   550  	})
   551  	if err != nil {
   552  		return skymodules.RenterContract{}, nil, errors.AddContext(err, "failed to send RPCRenewContractRenterSignatures to host")
   553  	}
   554  
   555  	// Read the host's signatures and add them to the transactions.
   556  	var hostSignatureResp modules.RPCRenewContractHostSignatures
   557  	err = modules.RPCReadMaxLen(conn, &hostSignatureResp, modules.RenewDecodeMaxLen)
   558  	if err != nil {
   559  		return skymodules.RenterContract{}, nil, errors.AddContext(err, "failed to read RPCRenewContractHostSignatures from host")
   560  	}
   561  	for _, sig := range hostSignatureResp.ContractSignatures {
   562  		_ = txnBuilder.AddTransactionSignature(sig)
   563  	}
   564  	noOpRevTxn.TransactionSignatures = append(noOpRevTxn.TransactionSignatures, hostSignatureResp.NoOpRevisionSignature)
   565  
   566  	// Construct the final transaction.
   567  	txnSet, err = prepareTransactionSet(txnBuilder)
   568  	if err != nil {
   569  		return skymodules.RenterContract{}, nil, errors.AddContext(err, "failed to prepare txnSet with finalRev and new contract")
   570  	}
   571  
   572  	// Submit the txn set with the final revision and new contract to the blockchain.
   573  	err = tpool.AcceptTransactionSet(txnSet)
   574  	if err == modules.ErrDuplicateTransactionSet {
   575  		// As long as it made it into the transaction pool, we're good.
   576  		err = nil
   577  	}
   578  	if err != nil {
   579  		return skymodules.RenterContract{}, nil, errors.AddContext(err, "failed to submit txnSet for renewal to blockchain")
   580  	}
   581  
   582  	// Construct contract header.
   583  	header := contractHeader{
   584  		Transaction:     noOpRevTxn,
   585  		SecretKey:       ourSKNew,
   586  		StartHeight:     startHeight,
   587  		TotalCost:       funding,
   588  		ContractFee:     pt.ContractPrice,
   589  		TxnFee:          txnFee,
   590  		SiafundFee:      types.Tax(startHeight, fc.Payout),
   591  		StorageSpending: basePrice,
   592  		Utility: skymodules.ContractUtility{
   593  			// keep the old contract's gfu utility. We don't want to
   594  			// accidentally increase the number of gfu contracts
   595  			// beyond the max. So contracts should only be marked as
   596  			// gfu by the contract maintenance.
   597  			GoodForUpload: oldContract.Utility.GoodForUpload,
   598  
   599  			// keep the old contract's goodForRenew and
   600  			// goodForRefresh utilities. We don't know if the
   601  			// contract got refreshed or renewed but since we
   602  			// renewed it one of both must the true.
   603  			GoodForRefresh: oldContract.Utility.GoodForRefresh,
   604  			GoodForRenew:   oldContract.Utility.GoodForRenew,
   605  		},
   606  	}
   607  
   608  	// Get old roots
   609  	oldRoots, err := oldSC.merkleRoots.merkleRoots()
   610  	if err != nil {
   611  		return skymodules.RenterContract{}, nil, err
   612  	}
   613  
   614  	// Add contract to set.
   615  	newContract, err := cs.managedInsertContract(header, oldRoots)
   616  	if err != nil {
   617  		return skymodules.RenterContract{}, nil, err
   618  	}
   619  
   620  	// Commit changes to old contract.
   621  	if err := oldSC.managedCommitClearContract(walTxn, finalRevTxn, renewCost); err != nil {
   622  		return skymodules.RenterContract{}, nil, err
   623  	}
   624  	return newContract, txnSet, nil
   625  }
   626  
   627  // createFileContractUnlockConds is a helper method to create unlock conditions
   628  // for forming and renewing a contract.
   629  func createFileContractUnlockConds(hpk types.SiaPublicKey, ourPK crypto.PublicKey) types.UnlockConditions {
   630  	return types.UnlockConditions{
   631  		PublicKeys: []types.SiaPublicKey{
   632  			types.Ed25519PublicKey(ourPK),
   633  			hpk,
   634  		},
   635  		SignaturesRequired: 2,
   636  	}
   637  }