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