gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/negotiate.go (about) 1 package modules 2 3 import ( 4 "bytes" 5 "crypto/cipher" 6 "errors" 7 "fmt" 8 "io" 9 "net" 10 "strings" 11 "time" 12 13 "gitlab.com/NebulousLabs/fastrand" 14 "gitlab.com/SiaPrime/SiaPrime/build" 15 "gitlab.com/SiaPrime/SiaPrime/crypto" 16 "gitlab.com/SiaPrime/SiaPrime/encoding" 17 "gitlab.com/SiaPrime/SiaPrime/types" 18 "golang.org/x/crypto/chacha20poly1305" 19 ) 20 21 const ( 22 // AcceptResponse is the response given to an RPC call to indicate 23 // acceptance, i.e. that the sender wishes to continue communication. 24 AcceptResponse = "accept" 25 26 // StopResponse is the response given to an RPC call to indicate graceful 27 // termination, i.e. that the sender wishes to cease communication, but 28 // not due to an error. 29 StopResponse = "stop" 30 ) 31 32 const ( 33 // NegotiateDownloadTime defines the amount of time that the renter and 34 // host have to negotiate a download request batch. The time is set high 35 // enough that two nodes behind Tor have a reasonable chance of completing 36 // the negotiation. 37 NegotiateDownloadTime = 600 * time.Second 38 39 // NegotiateFileContractRevisionTime defines the minimum amount of time 40 // that the renter and host have to negotiate a file contract revision. The 41 // time is set high enough that a full 4MB can be piped through a 42 // connection that is running over Tor. 43 NegotiateFileContractRevisionTime = 600 * time.Second 44 45 // NegotiateFileContractTime defines the amount of time that the renter and 46 // host have to negotiate a file contract. The time is set high enough that 47 // a node behind Tor has a reasonable chance at making the multiple 48 // required round trips to complete the negotiation. 49 NegotiateFileContractTime = 360 * time.Second 50 51 // NegotiateMaxDownloadActionRequestSize defines the maximum size that a 52 // download request can be. Note, this is not a max size for the data that 53 // can be requested, but instead is a max size for the definition of the 54 // data being requested. 55 NegotiateMaxDownloadActionRequestSize = 50e3 56 57 // NegotiateMaxErrorSize indicates the maximum number of bytes that can be 58 // used to encode an error being sent during negotiation. 59 NegotiateMaxErrorSize = 256 60 61 // NegotiateMaxFileContractRevisionSize specifies the maximum size that a 62 // file contract revision is allowed to have when being sent over the wire 63 // during negotiation. 64 NegotiateMaxFileContractRevisionSize = 3e3 65 66 // NegotiateMaxFileContractSetLen determines the maximum allowed size of a 67 // transaction set that can be sent when trying to negotiate a file 68 // contract. The transaction set will contain all of the unconfirmed 69 // dependencies of the file contract, meaning that it can be quite large. 70 // The transaction pool's size limit for transaction sets has been chosen 71 // as a reasonable guideline for determining what is too large. 72 NegotiateMaxFileContractSetLen = TransactionSetSizeLimit - 1e3 73 74 // NegotiateMaxHostExternalSettingsLen is the maximum allowed size of an 75 // encoded HostExternalSettings. 76 NegotiateMaxHostExternalSettingsLen = 16000 77 78 // NegotiateMaxSiaPubkeySize defines the maximum size that a SiaPubkey is 79 // allowed to be when being sent over the wire during negotiation. 80 NegotiateMaxSiaPubkeySize = 1e3 81 82 // NegotiateMaxTransactionSignatureSize defines the maximum size that a 83 // transaction signature is allowed to be when being sent over the wire 84 // during negotiation. 85 NegotiateMaxTransactionSignatureSize = 2e3 86 87 // NegotiateMaxTransactionSignaturesSize defines the maximum size that a 88 // transaction signature slice is allowed to be when being sent over the 89 // wire during negotiation. 90 NegotiateMaxTransactionSignaturesSize = 5e3 91 92 // NegotiateRecentRevisionTime establishes the minimum amount of time that 93 // the connection deadline is expected to be set to when a recent file 94 // contract revision is being requested from the host. The deadline is long 95 // enough that the connection should be successful even if both parties are 96 // running Tor. 97 NegotiateRecentRevisionTime = 120 * time.Second 98 99 // NegotiateRenewContractTime defines the minimum amount of time that the 100 // renter and host have to negotiate a final contract renewal. The time is 101 // high enough that the negotiation can occur over a Tor connection, and 102 // that both the host and the renter can have time to process large Merkle 103 // tree calculations that may be involved with renewing a file contract. 104 NegotiateRenewContractTime = 600 * time.Second 105 ) 106 107 var ( 108 // ErrInsufficientStorageForSector is returned if the host tries to add a 109 // sector when there is not enough storage remaining on the host to accept 110 // the sector. 111 // 112 // Ideally, the host will adjust pricing as the host starts to fill up, so 113 // this error should be pretty rare. Demand should drive the price up 114 // faster than the Host runs out of space, such that the host is always 115 // hovering around 95% capacity and rarely over 98% or under 90% capacity. 116 ErrInsufficientStorageForSector = errors.New("not enough storage remaining to accept sector") 117 ) 118 119 var ( 120 // NegotiateSettingsTime establishes the minimum amount of time that the 121 // connection deadline is expected to be set to when settings are being 122 // requested from the host. The deadline is long enough that the connection 123 // should be successful even if both parties are on Tor. 124 NegotiateSettingsTime = build.Select(build.Var{ 125 Dev: 120 * time.Second, 126 Standard: 120 * time.Second, 127 Testing: 3 * time.Second, 128 }).(time.Duration) 129 ) 130 131 var ( 132 // ActionDelete is the specifier for a RevisionAction that deletes a 133 // sector. 134 ActionDelete = types.Specifier{'D', 'e', 'l', 'e', 't', 'e'} 135 136 // ActionInsert is the specifier for a RevisionAction that inserts a 137 // sector. 138 ActionInsert = types.Specifier{'I', 'n', 's', 'e', 'r', 't'} 139 140 // ActionModify is the specifier for a RevisionAction that modifies sector 141 // data. 142 ActionModify = types.Specifier{'M', 'o', 'd', 'i', 'f', 'y'} 143 144 // ErrAnnNotAnnouncement indicates that the provided host announcement does 145 // not use a recognized specifier, indicating that it's either not a host 146 // announcement or it's not a recognized version of a host announcement. 147 ErrAnnNotAnnouncement = errors.New("provided data does not form a recognized host announcement") 148 149 // ErrAnnUnrecognizedSignature is returned when the signature in a host 150 // announcement is not a type of signature that is recognized. 151 ErrAnnUnrecognizedSignature = errors.New("the signature provided in the host announcement is not recognized") 152 153 // ErrRevisionCoveredFields is returned if there is a covered fields object 154 // in a transaction signature which has the 'WholeTransaction' field set to 155 // true, meaning that miner fees cannot be added to the transaction without 156 // invalidating the signature. 157 ErrRevisionCoveredFields = errors.New("file contract revision transaction signature does not allow miner fees to be added") 158 159 // ErrRevisionSigCount is returned when a file contract revision has the 160 // wrong number of transaction signatures. 161 ErrRevisionSigCount = errors.New("file contract revision has the wrong number of transaction signatures") 162 163 // ErrStopResponse is the error returned by ReadNegotiationAcceptance when 164 // it reads the StopResponse string. 165 ErrStopResponse = errors.New("sender wishes to stop communicating") 166 167 // PrefixHostAnnouncement is used to indicate that a transaction's 168 // Arbitrary Data field contains a host announcement. The encoded 169 // announcement will follow this prefix. 170 PrefixHostAnnouncement = types.Specifier{'H', 'o', 's', 't', 'A', 'n', 'n', 'o', 'u', 'n', 'c', 'e', 'm', 'e', 'n', 't'} 171 172 // PrefixFileContractIdentifier is used to indicate that a transaction's 173 // Arbitrary Data field contains a file contract identifier. The identifier 174 // and its signature will follow this prefix. 175 PrefixFileContractIdentifier = types.Specifier{'F', 'C', 'I', 'd', 'e', 'n', 't', 'i', 'f', 'i', 'e', 'r'} 176 177 // RPCDownload is the specifier for downloading a file from a host. 178 RPCDownload = types.Specifier{'D', 'o', 'w', 'n', 'l', 'o', 'a', 'd', 2} 179 180 // RPCFormContract is the specifier for forming a contract with a host. 181 RPCFormContract = types.Specifier{'F', 'o', 'r', 'm', 'C', 'o', 'n', 't', 'r', 'a', 'c', 't', 2} 182 183 // RPCRenewContract is the specifier to renewing an existing contract. 184 RPCRenewContract = types.Specifier{'R', 'e', 'n', 'e', 'w', 'C', 'o', 'n', 't', 'r', 'a', 'c', 't', 2} 185 186 // RPCReviseContract is the specifier for revising an existing file 187 // contract. 188 RPCReviseContract = types.Specifier{'R', 'e', 'v', 'i', 's', 'e', 'C', 'o', 'n', 't', 'r', 'a', 'c', 't', 2} 189 190 // RPCSettings is the specifier for requesting settings from the host. 191 RPCSettings = types.Specifier{'S', 'e', 't', 't', 'i', 'n', 'g', 's', 2} 192 193 // SectorSize defines how large a sector should be in bytes. The sector 194 // size needs to be a power of two to be compatible with package 195 // merkletree. 4MB has been chosen for the live network because large 196 // sectors significantly reduce the tracking overhead experienced by the 197 // renter and the host. 198 SectorSize = build.Select(build.Var{ 199 Dev: uint64(1 << 18), // 256 KiB 200 Standard: uint64(1 << 22), // 4 MiB 201 Testing: uint64(1 << 12), // 4 KiB 202 }).(uint64) 203 ) 204 205 type ( 206 // A DownloadAction is a description of a download that the renter would 207 // like to make. The MerkleRoot indicates the root of the sector, the 208 // offset indicates what portion of the sector is being downloaded, and the 209 // length indicates how many bytes should be grabbed starting from the 210 // offset. 211 DownloadAction struct { 212 MerkleRoot crypto.Hash 213 Offset uint64 214 Length uint64 215 } 216 217 // HostAnnouncement is an announcement by the host that appears in the 218 // blockchain. 'Specifier' is always 'PrefixHostAnnouncement'. The 219 // announcement is always followed by a signature from the public key of 220 // the whole announcement. 221 HostAnnouncement struct { 222 Specifier types.Specifier 223 NetAddress NetAddress 224 PublicKey types.SiaPublicKey 225 } 226 227 // HostExternalSettings are the parameters advertised by the host. These 228 // are the values that the renter will request from the host in order to 229 // build its database. 230 HostExternalSettings struct { 231 // MaxBatchSize indicates the maximum size in bytes that a batch is 232 // allowed to be. A batch is an array of revision actions; each 233 // revision action can have a different number of bytes, depending on 234 // the action, so the number of revision actions allowed depends on the 235 // sizes of each. 236 AcceptingContracts bool `json:"acceptingcontracts"` 237 MaxDownloadBatchSize uint64 `json:"maxdownloadbatchsize"` 238 MaxDuration types.BlockHeight `json:"maxduration"` 239 MaxReviseBatchSize uint64 `json:"maxrevisebatchsize"` 240 NetAddress NetAddress `json:"netaddress"` 241 RemainingStorage uint64 `json:"remainingstorage"` 242 SectorSize uint64 `json:"sectorsize"` 243 TotalStorage uint64 `json:"totalstorage"` 244 UnlockHash types.UnlockHash `json:"unlockhash"` 245 WindowSize types.BlockHeight `json:"windowsize"` 246 247 // Collateral is the amount of collateral that the host will put up for 248 // storage in 'bytes per block', as an assurance to the renter that the 249 // host really is committed to keeping the file. But, because the file 250 // contract is created with no data available, this does leave the host 251 // exposed to an attack by a wealthy renter whereby the renter causes 252 // the host to lockup in-advance a bunch of funds that the renter then 253 // never uses, meaning the host will not have collateral for other 254 // clients. 255 // 256 // MaxCollateral indicates the maximum number of coins that a host is 257 // willing to put into a file contract. 258 Collateral types.Currency `json:"collateral"` 259 MaxCollateral types.Currency `json:"maxcollateral"` 260 261 // ContractPrice is the number of coins that the renter needs to pay to 262 // the host just to open a file contract with them. Generally, the price 263 // is only to cover the siacoin fees that the host will suffer when 264 // submitting the file contract revision and storage proof to the 265 // blockchain. 266 // 267 // BaseRPC price is a flat per-RPC fee charged by the host for any 268 // non-free RPC. 269 // 270 // 'Download' bandwidth price is the cost per byte of downloading data 271 // from the host. This includes metadata such as Merkle proofs. 272 // 273 // SectorAccessPrice is the cost per sector of data accessed when 274 // downloading data. 275 // 276 // StoragePrice is the cost per-byte-per-block in hastings of storing 277 // data on the host. 278 // 279 // 'Upload' bandwidth price is the cost per byte of uploading data to 280 // the host. 281 BaseRPCPrice types.Currency `json:"baserpcprice"` 282 ContractPrice types.Currency `json:"contractprice"` 283 DownloadBandwidthPrice types.Currency `json:"downloadbandwidthprice"` 284 SectorAccessPrice types.Currency `json:"sectoraccessprice"` 285 StoragePrice types.Currency `json:"storageprice"` 286 UploadBandwidthPrice types.Currency `json:"uploadbandwidthprice"` 287 288 // Because the host has a public key, and settings are signed, and 289 // because settings may be MITM'd, settings need a revision number so 290 // that a renter can compare multiple sets of settings and determine 291 // which is the most recent. 292 RevisionNumber uint64 `json:"revisionnumber"` 293 Version string `json:"version"` 294 } 295 296 // HostOldExternalSettings are the pre-v1.4.0 host settings. 297 HostOldExternalSettings struct { 298 AcceptingContracts bool `json:"acceptingcontracts"` 299 MaxDownloadBatchSize uint64 `json:"maxdownloadbatchsize"` 300 MaxDuration types.BlockHeight `json:"maxduration"` 301 MaxReviseBatchSize uint64 `json:"maxrevisebatchsize"` 302 NetAddress NetAddress `json:"netaddress"` 303 RemainingStorage uint64 `json:"remainingstorage"` 304 SectorSize uint64 `json:"sectorsize"` 305 TotalStorage uint64 `json:"totalstorage"` 306 UnlockHash types.UnlockHash `json:"unlockhash"` 307 WindowSize types.BlockHeight `json:"windowsize"` 308 Collateral types.Currency `json:"collateral"` 309 MaxCollateral types.Currency `json:"maxcollateral"` 310 ContractPrice types.Currency `json:"contractprice"` 311 DownloadBandwidthPrice types.Currency `json:"downloadbandwidthprice"` 312 StoragePrice types.Currency `json:"storageprice"` 313 UploadBandwidthPrice types.Currency `json:"uploadbandwidthprice"` 314 RevisionNumber uint64 `json:"revisionnumber"` 315 Version string `json:"version"` 316 } 317 318 // A RevisionAction is a description of an edit to be performed on a file 319 // contract. Three types are allowed, 'ActionDelete', 'ActionInsert', and 320 // 'ActionModify'. ActionDelete just takes a sector index, indicating which 321 // sector is going to be deleted. ActionInsert takes a sector index, and a 322 // full sector of data, indicating that a sector at the index should be 323 // inserted with the provided data. 'Modify' revises the sector at the 324 // given index, rewriting it with the provided data starting from the 325 // 'offset' within the sector. 326 // 327 // Modify could be simulated with an insert and a delete, however an insert 328 // requires a full sector to be uploaded, and a modify can be just a few 329 // kb, which can be significantly faster. 330 RevisionAction struct { 331 Type types.Specifier 332 SectorIndex uint64 333 Offset uint64 334 Data []byte 335 } 336 ) 337 338 // New RPC IDs 339 var ( 340 RPCLoopEnter = types.Specifier{'L', 'o', 'o', 'p', 'E', 'n', 't', 'e', 'r'} 341 RPCLoopExit = types.Specifier{'L', 'o', 'o', 'p', 'E', 'x', 'i', 't'} 342 RPCLoopFormContract = types.Specifier{'L', 'o', 'o', 'p', 'F', 'o', 'r', 'm', 'C', 'o', 'n', 't', 'r', 'a', 'c', 't'} 343 RPCLoopLock = types.Specifier{'L', 'o', 'o', 'p', 'L', 'o', 'c', 'k'} 344 RPCLoopRead = types.Specifier{'L', 'o', 'o', 'p', 'R', 'e', 'a', 'd'} 345 RPCLoopRenewContract = types.Specifier{'L', 'o', 'o', 'p', 'R', 'e', 'n', 'e', 'w'} 346 RPCLoopSectorRoots = types.Specifier{'L', 'o', 'o', 'p', 'S', 'e', 'c', 't', 'o', 'r', 'R', 'o', 'o', 't', 's'} 347 RPCLoopSettings = types.Specifier{'L', 'o', 'o', 'p', 'S', 'e', 't', 't', 'i', 'n', 'g', 's'} 348 RPCLoopUnlock = types.Specifier{'L', 'o', 'o', 'p', 'U', 'n', 'l', 'o', 'c', 'k'} 349 RPCLoopWrite = types.Specifier{'L', 'o', 'o', 'p', 'W', 'r', 'i', 't', 'e'} 350 ) 351 352 // RPC ciphers 353 var ( 354 CipherChaCha20Poly1305 = types.Specifier{'C', 'h', 'a', 'C', 'h', 'a', '2', '0', 'P', 'o', 'l', 'y', '1', '3', '0', '5'} 355 CipherNoOverlap = types.Specifier{'N', 'o', 'O', 'v', 'e', 'r', 'l', 'a', 'p'} 356 ) 357 358 // Write actions 359 var ( 360 WriteActionAppend = types.Specifier{'A', 'p', 'p', 'e', 'n', 'd'} 361 WriteActionTrim = types.Specifier{'T', 'r', 'i', 'm'} 362 WriteActionSwap = types.Specifier{'S', 'w', 'a', 'p'} 363 WriteActionUpdate = types.Specifier{'U', 'p', 'd', 'a', 't', 'e'} 364 ) 365 366 // Read interrupt 367 var ( 368 RPCLoopReadStop = types.Specifier{'R', 'e', 'a', 'd', 'S', 't', 'o', 'p'} 369 ) 370 371 var ( 372 // RPCChallengePrefix is the prefix prepended to the challenge data 373 // supplied by the host when proving ownership of a contract's secret key. 374 RPCChallengePrefix = types.Specifier{'c', 'h', 'a', 'l', 'l', 'e', 'n', 'g', 'e'} 375 ) 376 377 // New RPC request and response types 378 type ( 379 // An RPCError may be sent instead of a Response to any RPC. 380 RPCError struct { 381 Type types.Specifier 382 Data []byte // structure depends on Type 383 Description string // human-readable error string 384 } 385 386 // LoopKeyExchangeRequest is the first object sent when initializing the 387 // renter-host protocol. 388 LoopKeyExchangeRequest struct { 389 // The renter's ephemeral X25519 public key. 390 PublicKey crypto.X25519PublicKey 391 392 // Encryption ciphers that the renter supports. 393 Ciphers []types.Specifier 394 } 395 396 // LoopKeyExchangeResponse contains the host's response to the 397 // KeyExchangeRequest. 398 LoopKeyExchangeResponse struct { 399 // The host's ephemeral X25519 public key. 400 PublicKey crypto.X25519PublicKey 401 402 // Signature of (Host's Public Key | Renter's Public Key). Note that this 403 // also serves to authenticate the host. 404 Signature []byte 405 406 // Cipher selected by the host. Must be one of the ciphers offered in 407 // the key exchange request. 408 Cipher types.Specifier 409 } 410 411 // LoopChallengeRequest contains a challenge for the renter to prove their 412 // identity. It is the host's first encrypted message, and immediately 413 // follows KeyExchangeResponse. 414 LoopChallengeRequest struct { 415 // Entropy signed by the renter to prove that it controls the secret key 416 // used to sign contract revisions. The actual data signed should be: 417 // 418 // blake2b(RPCChallengePrefix | Challenge) 419 Challenge [16]byte 420 } 421 422 // LoopLockRequest contains the request parameters for RPCLoopLock. 423 LoopLockRequest struct { 424 // The contract to lock; implicitly referenced by subsequent RPCs. 425 ContractID types.FileContractID 426 427 // The host's challenge, signed by the renter's contract key. 428 Signature []byte 429 430 // Lock timeout, in milliseconds. 431 Timeout uint64 432 } 433 434 // LoopLockResponse contains the response data for RPCLoopLock. 435 LoopLockResponse struct { 436 Acquired bool 437 NewChallenge [16]byte 438 Revision types.FileContractRevision 439 Signatures []types.TransactionSignature 440 } 441 442 // LoopReadRequestSection is a section requested in LoopReadRequest. 443 LoopReadRequestSection struct { 444 MerkleRoot [32]byte 445 Offset uint32 446 Length uint32 447 } 448 449 // LoopReadRequest contains the request parameters for RPCLoopRead. 450 LoopReadRequest struct { 451 Sections []LoopReadRequestSection 452 MerkleProof bool 453 454 NewRevisionNumber uint64 455 NewValidProofValues []types.Currency 456 NewMissedProofValues []types.Currency 457 Signature []byte 458 } 459 460 // LoopReadResponse contains the response data for RPCLoopRead. 461 LoopReadResponse struct { 462 Signature []byte 463 Data []byte 464 MerkleProof []crypto.Hash 465 } 466 467 // LoopSectorRootsRequest contains the request parameters for RPCLoopSectorRoots. 468 LoopSectorRootsRequest struct { 469 RootOffset uint64 470 NumRoots uint64 471 472 NewRevisionNumber uint64 473 NewValidProofValues []types.Currency 474 NewMissedProofValues []types.Currency 475 Signature []byte 476 } 477 478 // LoopSectorRootsResponse contains the response data for RPCLoopSectorRoots. 479 LoopSectorRootsResponse struct { 480 Signature []byte 481 SectorRoots []crypto.Hash 482 MerkleProof []crypto.Hash 483 } 484 485 // LoopFormContractRequest contains the request parameters for RPCLoopFormContract. 486 LoopFormContractRequest struct { 487 Transactions []types.Transaction 488 RenterKey types.SiaPublicKey 489 } 490 491 // LoopContractAdditions contains the parent transaction, inputs, and 492 // outputs added by the host when negotiating a file contract. 493 LoopContractAdditions struct { 494 Parents []types.Transaction 495 Inputs []types.SiacoinInput 496 Outputs []types.SiacoinOutput 497 } 498 499 // LoopContractSignatures contains the signatures for a contract 500 // transaction and initial revision. These signatures are sent by both the 501 // renter and host during contract formation and renewal. 502 LoopContractSignatures struct { 503 ContractSignatures []types.TransactionSignature 504 RevisionSignature types.TransactionSignature 505 } 506 507 // LoopRenewContractRequest contains the request parameters for RPCLoopRenewContract. 508 LoopRenewContractRequest struct { 509 Transactions []types.Transaction 510 RenterKey types.SiaPublicKey 511 } 512 513 // LoopSettingsResponse contains the response data for RPCLoopSettingsResponse. 514 LoopSettingsResponse struct { 515 Settings []byte // actually a JSON-encoded HostExternalSettings 516 } 517 518 // LoopWriteRequest contains the request parameters for RPCLoopWrite. 519 LoopWriteRequest struct { 520 Actions []LoopWriteAction 521 MerkleProof bool 522 523 NewRevisionNumber uint64 524 NewValidProofValues []types.Currency 525 NewMissedProofValues []types.Currency 526 } 527 528 // LoopWriteAction is a generic Write action. The meaning of each field 529 // depends on the Type of the action. 530 LoopWriteAction struct { 531 Type types.Specifier 532 A, B uint64 533 Data []byte 534 } 535 536 // LoopWriteMerkleProof contains the optional Merkle proof for response data 537 // for RPCLoopWrite. 538 LoopWriteMerkleProof struct { 539 OldSubtreeHashes []crypto.Hash 540 OldLeafHashes []crypto.Hash 541 NewMerkleRoot crypto.Hash 542 } 543 544 // LoopWriteResponse contains the response data for RPCLoopWrite. 545 LoopWriteResponse struct { 546 Signature []byte 547 } 548 ) 549 550 // Error implements the error interface. 551 func (e *RPCError) Error() string { 552 return e.Description 553 } 554 555 // RPCMinLen is the minimum size of an RPC message. If an encoded message 556 // would be smaller than RPCMinLen, it is padded with random data. 557 const RPCMinLen = 4096 558 559 // WriteRPCMessage writes an encrypted RPC message. 560 func WriteRPCMessage(w io.Writer, aead cipher.AEAD, obj interface{}) error { 561 payload := encoding.Marshal(obj) 562 // pad the payload to RPCMinLen bytes to prevent eavesdroppers from 563 // identifying RPCs by their size. 564 minLen := RPCMinLen - aead.Overhead() - aead.NonceSize() 565 if len(payload) < minLen { 566 payload = append(payload, fastrand.Bytes(minLen-len(payload))...) 567 } 568 return encoding.WritePrefixedBytes(w, crypto.EncryptWithNonce(payload, aead)) 569 } 570 571 // ReadRPCMessage reads an encrypted RPC message. 572 func ReadRPCMessage(r io.Reader, aead cipher.AEAD, obj interface{}, maxLen uint64) error { 573 ciphertext, err := encoding.ReadPrefixedBytes(r, maxLen) 574 if err != nil { 575 return err 576 } 577 plaintext, err := crypto.DecryptWithNonce(ciphertext, aead) 578 if err != nil { 579 return err 580 } 581 return encoding.Unmarshal(plaintext, obj) 582 } 583 584 // WriteRPCRequest writes an encrypted RPC request using the new loop 585 // protocol. 586 func WriteRPCRequest(w io.Writer, aead cipher.AEAD, rpcID types.Specifier, req interface{}) error { 587 if err := WriteRPCMessage(w, aead, rpcID); err != nil { 588 return err 589 } 590 if req != nil { 591 return WriteRPCMessage(w, aead, req) 592 } 593 return nil 594 } 595 596 // rpcResponse is a helper type for encoding and decoding RPC response messages. 597 type rpcResponse struct { 598 err *RPCError 599 data interface{} 600 } 601 602 func (resp *rpcResponse) MarshalSia(w io.Writer) error { 603 if resp.data == nil { 604 resp.data = struct{}{} 605 } 606 return encoding.NewEncoder(w).EncodeAll(resp.err, resp.data) 607 } 608 609 func (resp *rpcResponse) UnmarshalSia(r io.Reader) error { 610 // NOTE: no allocation limit is required because this method is always 611 // called via encoding.Unmarshal, which already imposes an allocation limit. 612 d := encoding.NewDecoder(r, 0) 613 if err := d.Decode(&resp.err); err != nil { 614 return err 615 } else if resp.err != nil { 616 return resp.err 617 } 618 return d.Decode(resp.data) 619 } 620 621 // WriteRPCResponse writes an RPC response or error using the new loop 622 // protocol. Either resp or err must be nil. If err is an *RPCError, it is 623 // sent directly; otherwise, a generic RPCError is created from err's Error 624 // string. 625 func WriteRPCResponse(w io.Writer, aead cipher.AEAD, resp interface{}, err error) error { 626 re, ok := err.(*RPCError) 627 if err != nil && !ok { 628 re = &RPCError{Description: err.Error()} 629 } 630 return WriteRPCMessage(w, aead, &rpcResponse{re, resp}) 631 } 632 633 // ReadRPCID reads an RPC request ID using the new loop protocol. 634 func ReadRPCID(r io.Reader, aead cipher.AEAD) (rpcID types.Specifier, err error) { 635 err = ReadRPCMessage(r, aead, &rpcID, RPCMinLen) 636 return 637 } 638 639 // ReadRPCRequest reads an RPC request using the new loop protocol. 640 func ReadRPCRequest(r io.Reader, aead cipher.AEAD, req interface{}, maxLen uint64) error { 641 return ReadRPCMessage(r, aead, req, maxLen) 642 } 643 644 // ReadRPCResponse reads an RPC response using the new loop protocol. 645 func ReadRPCResponse(r io.Reader, aead cipher.AEAD, resp interface{}, maxLen uint64) error { 646 if maxLen < RPCMinLen { 647 build.Critical("maxLen must be at least RPCMinLen") 648 maxLen = RPCMinLen 649 } 650 return ReadRPCMessage(r, aead, &rpcResponse{nil, resp}, maxLen) 651 } 652 653 // A RenterHostSession is a session of the new renter-host protocol. 654 type RenterHostSession struct { 655 aead cipher.AEAD 656 conn net.Conn 657 } 658 659 // WriteRequest writes an encrypted RPC request using the new loop 660 // protocol. 661 func (s *RenterHostSession) WriteRequest(rpcID types.Specifier, req interface{}) error { 662 return WriteRPCRequest(s.conn, s.aead, rpcID, req) 663 } 664 665 // WriteResponse writes an RPC response or error using the new loop 666 // protocol. Either resp or err must be nil. If err is an *RPCError, it is 667 // sent directly; otherwise, a generic RPCError is created from err's Error 668 // string. 669 func (s *RenterHostSession) WriteResponse(resp interface{}, err error) error { 670 return WriteRPCResponse(s.conn, s.aead, resp, err) 671 } 672 673 // ReadRPCID reads an RPC request ID using the new loop protocol. 674 func (s *RenterHostSession) ReadRPCID() (rpcID types.Specifier, err error) { 675 return ReadRPCID(s.conn, s.aead) 676 } 677 678 // ReadRequest reads an RPC request using the new loop protocol. 679 func (s *RenterHostSession) ReadRequest(req interface{}, maxLen uint64) error { 680 return ReadRPCRequest(s.conn, s.aead, req, maxLen) 681 } 682 683 // ReadResponse reads an RPC response using the new loop protocol. 684 func (s *RenterHostSession) ReadResponse(resp interface{}, maxLen uint64) error { 685 return ReadRPCResponse(s.conn, s.aead, resp, maxLen) 686 } 687 688 // NewRenterSession returns a new renter-side session of the renter-host 689 // protocol. 690 func NewRenterSession(conn net.Conn, hostPublicKey types.SiaPublicKey) (*RenterHostSession, LoopChallengeRequest, error) { 691 // generate a session key 692 xsk, xpk := crypto.GenerateX25519KeyPair() 693 694 // send our half of the key exchange 695 req := LoopKeyExchangeRequest{ 696 PublicKey: xpk, 697 Ciphers: []types.Specifier{CipherChaCha20Poly1305}, 698 } 699 if err := encoding.NewEncoder(conn).EncodeAll(RPCLoopEnter, req); err != nil { 700 return nil, LoopChallengeRequest{}, err 701 } 702 // read host's half of the key exchange 703 var resp LoopKeyExchangeResponse 704 if err := encoding.NewDecoder(conn, encoding.DefaultAllocLimit).Decode(&resp); err != nil { 705 return nil, LoopChallengeRequest{}, err 706 } 707 // validate the signature before doing anything else; don't want to punish 708 // the "host" if we're talking to an imposter 709 var hpk crypto.PublicKey 710 copy(hpk[:], hostPublicKey.Key) 711 var sig crypto.Signature 712 copy(sig[:], resp.Signature) 713 if err := crypto.VerifyHash(crypto.HashAll(req.PublicKey, resp.PublicKey), hpk, sig); err != nil { 714 return nil, LoopChallengeRequest{}, err 715 } 716 // check for compatible cipher 717 if resp.Cipher != CipherChaCha20Poly1305 { 718 return nil, LoopChallengeRequest{}, errors.New("host selected unsupported cipher") 719 } 720 // derive shared secret, which we'll use as an encryption key 721 cipherKey := crypto.DeriveSharedSecret(xsk, resp.PublicKey) 722 723 // use cipherKey to initialize an AEAD cipher 724 aead, err := chacha20poly1305.New(cipherKey[:]) 725 if err != nil { 726 build.Critical("could not create cipher") 727 return nil, LoopChallengeRequest{}, err 728 } 729 730 // read host's challenge 731 var challengeReq LoopChallengeRequest 732 if err := ReadRPCMessage(conn, aead, &challengeReq, RPCMinLen); err != nil { 733 return nil, LoopChallengeRequest{}, err 734 } 735 return &RenterHostSession{ 736 aead: aead, 737 conn: conn, 738 }, challengeReq, nil 739 } 740 741 // ReadNegotiationAcceptance reads an accept/reject response from r (usually a 742 // net.Conn). If the response is not AcceptResponse, ReadNegotiationAcceptance 743 // returns the response as an error. If the response is StopResponse, 744 // ErrStopResponse is returned, allowing for direct error comparison. 745 // 746 // Note that since errors returned by ReadNegotiationAcceptance are newly 747 // allocated, they cannot be compared to other errors in the traditional 748 // fashion. 749 func ReadNegotiationAcceptance(r io.Reader) error { 750 var resp string 751 err := encoding.ReadObject(r, &resp, NegotiateMaxErrorSize) 752 if err != nil { 753 return err 754 } 755 switch resp { 756 case AcceptResponse: 757 return nil 758 case StopResponse: 759 return ErrStopResponse 760 default: 761 return errors.New(resp) 762 } 763 } 764 765 // WriteNegotiationAcceptance writes the 'accept' response to w (usually a 766 // net.Conn). 767 func WriteNegotiationAcceptance(w io.Writer) error { 768 return encoding.WriteObject(w, AcceptResponse) 769 } 770 771 // WriteNegotiationRejection will write a rejection response to w (usually a 772 // net.Conn) and return the input error. If the write fails, the write error 773 // is joined with the input error. 774 func WriteNegotiationRejection(w io.Writer, err error) error { 775 writeErr := encoding.WriteObject(w, err.Error()) 776 if writeErr != nil { 777 return build.JoinErrors([]error{err, writeErr}, "; ") 778 } 779 return err 780 } 781 782 // WriteNegotiationStop writes the 'stop' response to w (usually a 783 // net.Conn). 784 func WriteNegotiationStop(w io.Writer) error { 785 return encoding.WriteObject(w, StopResponse) 786 } 787 788 // CreateAnnouncement will take a host announcement and encode it, returning 789 // the exact []byte that should be added to the arbitrary data of a 790 // transaction. 791 func CreateAnnouncement(addr NetAddress, pk types.SiaPublicKey, sk crypto.SecretKey) (signedAnnouncement []byte, err error) { 792 if err := addr.IsValid(); err != nil { 793 return nil, err 794 } 795 796 // Create the HostAnnouncement and marshal it. 797 annBytes := encoding.Marshal(HostAnnouncement{ 798 Specifier: PrefixHostAnnouncement, 799 NetAddress: addr, 800 PublicKey: pk, 801 }) 802 803 // Create a signature for the announcement. 804 annHash := crypto.HashBytes(annBytes) 805 sig := crypto.SignHash(annHash, sk) 806 // Return the signed announcement. 807 return append(annBytes, sig[:]...), nil 808 } 809 810 // DecodeAnnouncement decodes announcement bytes into a host announcement, 811 // verifying the prefix and the signature. 812 func DecodeAnnouncement(fullAnnouncement []byte) (na NetAddress, spk types.SiaPublicKey, err error) { 813 // Read the first part of the announcement to get the intended host 814 // announcement. 815 var ha HostAnnouncement 816 dec := encoding.NewDecoder(bytes.NewReader(fullAnnouncement), len(fullAnnouncement)*3) 817 err = dec.Decode(&ha) 818 if err != nil { 819 return "", types.SiaPublicKey{}, err 820 } 821 822 // Check that the announcement was registered as a host announcement. 823 if ha.Specifier != PrefixHostAnnouncement { 824 return "", types.SiaPublicKey{}, ErrAnnNotAnnouncement 825 } 826 // Check that the public key is a recognized type of public key. 827 if ha.PublicKey.Algorithm != types.SignatureEd25519 { 828 return "", types.SiaPublicKey{}, ErrAnnUnrecognizedSignature 829 } 830 831 // Read the signature out of the reader. 832 var sig crypto.Signature 833 err = dec.Decode(&sig) 834 if err != nil { 835 return "", types.SiaPublicKey{}, err 836 } 837 // Verify the signature. 838 var pk crypto.PublicKey 839 copy(pk[:], ha.PublicKey.Key) 840 annHash := crypto.HashObject(ha) 841 err = crypto.VerifyHash(annHash, pk, sig) 842 if err != nil { 843 return "", types.SiaPublicKey{}, err 844 } 845 return ha.NetAddress, ha.PublicKey, nil 846 } 847 848 // IsOOSErr is a helper function to determine whether an error is a 849 // ErrInsufficientStorageForSector. 850 func IsOOSErr(err error) bool { 851 return strings.Contains(err.Error(), ErrInsufficientStorageForSector.Error()) 852 } 853 854 // VerifyFileContractRevisionTransactionSignatures checks that the signatures 855 // on a file contract revision are valid and cover the right fields. 856 func VerifyFileContractRevisionTransactionSignatures(fcr types.FileContractRevision, tsigs []types.TransactionSignature, height types.BlockHeight) error { 857 if len(tsigs) != 2 { 858 return ErrRevisionSigCount 859 } 860 for _, tsig := range tsigs { 861 // The transaction needs to be malleable so that miner fees can be 862 // added. If the whole transaction is covered, it is doomed to have no 863 // fees. 864 if tsig.CoveredFields.WholeTransaction { 865 return ErrRevisionCoveredFields 866 } 867 } 868 txn := types.Transaction{ 869 FileContractRevisions: []types.FileContractRevision{fcr}, 870 TransactionSignatures: tsigs, 871 } 872 // Check that the signatures verify. This will also check that the covered 873 // fields object is not over-aggressive, because if the object is pointing 874 // to elements that haven't been added to the transaction, verification 875 // will fail. 876 return txn.StandaloneValid(height) 877 } 878 879 // RenterPayoutsPreTax calculates the renterPayout before tax and the hostPayout 880 // given a host, the available renter funding, the expected txnFee for the 881 // transaction and an optional basePrice in case this helper is used for a 882 // renewal. It also returns the hostCollateral. 883 func RenterPayoutsPreTax(host HostDBEntry, funding, txnFee, basePrice, baseCollateral types.Currency, period types.BlockHeight, expectedStorage uint64) (renterPayout, hostPayout, hostCollateral types.Currency, err error) { 884 // Divide by zero check. 885 if host.StoragePrice.IsZero() { 886 host.StoragePrice = types.NewCurrency64(1) 887 } 888 // Underflow check. 889 if funding.Cmp(host.ContractPrice.Add(txnFee).Add(basePrice)) <= 0 { 890 err = fmt.Errorf("contract price (%v) plus transaction fee (%v) plus base price (%v) exceeds funding (%v)", 891 host.ContractPrice.HumanString(), txnFee.HumanString(), basePrice.HumanString(), funding.HumanString()) 892 return 893 } 894 // Calculate renterPayout. 895 renterPayout = funding.Sub(host.ContractPrice).Sub(txnFee).Sub(basePrice) 896 // Calculate hostCollateral by calculating the maximum amount of storage 897 // the renter can afford with 'funding' and calculating how much collateral 898 // the host wouldl have to put into the contract for that. We also add a 899 // potential baseCollateral. 900 maxStorageSizeTime := renterPayout.Div(host.StoragePrice) 901 hostCollateral = maxStorageSizeTime.Mul(host.Collateral).Add(baseCollateral) 902 // Don't add more collateral than 10x the collateral for the expected 903 // storage to save on fees. 904 maxRenterCollateral := host.Collateral.Mul64(uint64(period)).Mul64(expectedStorage).Mul64(5) 905 if hostCollateral.Cmp(maxRenterCollateral) > 0 { 906 hostCollateral = maxRenterCollateral 907 } 908 // Don't add more collateral than the host is willing to put into a single 909 // contract. 910 if hostCollateral.Cmp(host.MaxCollateral) > 0 { 911 hostCollateral = host.MaxCollateral 912 } 913 // Calculate hostPayout. 914 hostPayout = hostCollateral.Add(host.ContractPrice).Add(basePrice) 915 return 916 }