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 }