// managedFinalizeContract will take a file contract, add the host's // collateral, and then try submitting the file contract to the transaction // pool. If there is no error, the completed transaction set will be returned // to the caller. func (h *Host) managedFinalizeContract(builder modules.TransactionBuilder, renterPK crypto.PublicKey, renterSignatures []types.TransactionSignature, renterRevisionSignature types.TransactionSignature, initialSectorRoots []crypto.Hash, hostCollateral, hostInitialRevenue, hostInitialRisk types.Currency) ([]types.TransactionSignature, types.TransactionSignature, types.FileContractID, error) { for _, sig := range renterSignatures { builder.AddTransactionSignature(sig) } fullTxnSet, err := builder.Sign(true) if err != nil { builder.Drop() return nil, types.TransactionSignature{}, types.FileContractID{}, err } // Verify that the signature for the revision from the renter is correct. h.mu.RLock() blockHeight := h.blockHeight hostSPK := h.publicKey hostSK := h.secretKey h.mu.RUnlock() contractTxn := fullTxnSet[len(fullTxnSet)-1] fc := contractTxn.FileContracts[0] noOpRevision := types.FileContractRevision{ ParentID: contractTxn.FileContractID(0), UnlockConditions: types.UnlockConditions{ PublicKeys: []types.SiaPublicKey{ { Algorithm: types.SignatureEd25519, Key: renterPK[:], }, hostSPK, }, SignaturesRequired: 2, }, NewRevisionNumber: fc.RevisionNumber + 1, NewFileSize: fc.FileSize, NewFileMerkleRoot: fc.FileMerkleRoot, NewWindowStart: fc.WindowStart, NewWindowEnd: fc.WindowEnd, NewValidProofOutputs: fc.ValidProofOutputs, NewMissedProofOutputs: fc.MissedProofOutputs, NewUnlockHash: fc.UnlockHash, } // createRevisionSignature will also perform validation on the result, // returning an error if the renter provided an incorrect signature. revisionTransaction, err := createRevisionSignature(noOpRevision, renterRevisionSignature, hostSK, blockHeight) if err != nil { return nil, types.TransactionSignature{}, types.FileContractID{}, err } // Create and add the storage obligation for this file contract. fullTxn, _ := builder.View() so := storageObligation{ SectorRoots: initialSectorRoots, ContractCost: h.settings.MinContractPrice, LockedCollateral: hostCollateral, PotentialStorageRevenue: hostInitialRevenue, RiskedCollateral: hostInitialRisk, OriginTransactionSet: fullTxnSet, RevisionTransactionSet: []types.Transaction{revisionTransaction}, } // Get a lock on the storage obligation. lockErr := h.managedTryLockStorageObligation(so.id()) if lockErr != nil { build.Critical("failed to get a lock on a brand new storage obligation") return nil, types.TransactionSignature{}, types.FileContractID{}, lockErr } defer func() { if err != nil { h.managedUnlockStorageObligation(so.id()) } }() // addStorageObligation will submit the transaction to the transaction // pool, and will only do so if there was not some error in creating the // storage obligation. If the transaction pool returns a consensus // conflict, wait 30 seconds and try again. err = func() error { // Try adding the storage obligation. If there's an error, wait a few // seconds and try again. Eventually time out. It should be noted that // the storage obligation locking is both crappy and incomplete, and // that I'm not sure how this timeout plays with the overall host // timeouts. // // The storage obligation locks should occur at the highest level, not // just when the actual modification is happening. i := 0 for { h.mu.Lock() err = h.addStorageObligation(so) h.mu.Unlock() if err == nil { return nil } if err != nil && i > 4 { h.log.Println(err) builder.Drop() return err } i++ if build.Release == "standard" { time.Sleep(time.Second * 15) } } }() if err != nil { return nil, types.TransactionSignature{}, types.FileContractID{}, err } // Get the host's transaction signatures from the builder. var hostTxnSignatures []types.TransactionSignature _, _, _, txnSigIndices := builder.ViewAdded() for _, sigIndex := range txnSigIndices { hostTxnSignatures = append(hostTxnSignatures, fullTxn.TransactionSignatures[sigIndex]) } return hostTxnSignatures, revisionTransaction.TransactionSignatures[1], so.id(), nil }