github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/negotiate.go (about) 1 package modules 2 3 import ( 4 "bytes" 5 "errors" 6 "io" 7 "time" 8 9 "github.com/NebulousLabs/Sia/build" 10 "github.com/NebulousLabs/Sia/crypto" 11 "github.com/NebulousLabs/Sia/encoding" 12 "github.com/NebulousLabs/Sia/types" 13 ) 14 15 const ( 16 // AcceptResponse is the response given to an RPC call to indicate 17 // acceptance, i.e. that the sender wishes to continue communication. 18 AcceptResponse = "accept" 19 20 // StopResponse is the response given to an RPC call to indicate graceful 21 // termination, i.e. that the sender wishes to cease communication, but 22 // not due to an error. 23 StopResponse = "stop" 24 25 // NegotiateDownloadTime defines the amount of time that the renter and 26 // host have to negotitate a download request batch. The time is set high 27 // enough that two nodes behind Tor have a reasonable chance of completing 28 // the negotiation. 29 NegotiateDownloadTime = 600 * time.Second 30 31 // NegotiateFileContractTime defines the amount of time that the renter and 32 // host have to negotiate a file contract. The time is set high enough that 33 // a node behind Tor has a reasonable chance at making the multiple 34 // required round trips to complete the negotiation. 35 NegotiateFileContractTime = 360 * time.Second 36 37 // NegotiateFileContractRevisionTime defines the minimum amount of time 38 // that the renter and host have to negotiate a file contract revision. The 39 // time is set high enough that a full 4MB can be piped through a 40 // connection that is running over Tor. 41 NegotiateFileContractRevisionTime = 600 * time.Second 42 43 // NegotiateRecentRevisionTime establishes the minimum amount of time that 44 // the connection deadline is expected to be set to when a recent file 45 // contract revision is being requested from the host. The deadline is long 46 // enough that the connection should be successful even if both parties are 47 // running Tor. 48 NegotiateRecentRevisionTime = 120 * time.Second 49 50 // NegotiateRenewContractTime defines the minimum amount of time that the 51 // renter and host have to negotiate a final contract renewal. The time is 52 // high enough that the negotiation can occur over a Tor connection, and 53 // that both the host and the renter can have time to process large Merkle 54 // tree calculations that may be involved with renewing a file contract. 55 NegotiateRenewContractTime = 600 * time.Second 56 57 // NegotiateSettingsTime establishes the minimum amount of time that the 58 // connection deadline is expected to be set to when settings are being 59 // requested from the host. The deadline is long enough that the connection 60 // should be successful even if both parties are on Tor. 61 NegotiateSettingsTime = 120 * time.Second 62 63 // NegotiateMaxDownloadActionRequestSize defines the maximum size that a 64 // download request can be. Note, this is not a max size for the data that 65 // can be requested, but instead is a max size for the definition of the 66 // data being requested. 67 NegotiateMaxDownloadActionRequestSize = 50e3 68 69 // NegotiateMaxErrorSize indicates the maximum number of bytes that can be 70 // used to encode an error being sent during negotiation. 71 NegotiateMaxErrorSize = 256 72 73 // NegotiateMaxFileContractRevisionSize specifies the maximum size that a 74 // file contract revision is allowed to have when being sent over the wire 75 // during negotiation. 76 NegotiateMaxFileContractRevisionSize = 3e3 77 78 // NegotiateMaxFileContractSetLen determines the maximum allowed size of a 79 // transaction set that can be sent when trying to negotiate a file 80 // contract. The transaction set will contain all of the unconfirmed 81 // dependencies of the file contract, meaning that it can be quite large. 82 // The transaction pool's size limit for transaction sets has been chosen 83 // as a reasonable guideline for determining what is too large. 84 NegotiateMaxFileContractSetLen = TransactionSetSizeLimit - 1e3 85 86 // NegotiateMaxHostExternalSettingsLen is the maximum allowed size of an 87 // encoded HostExternalSettings. 88 NegotiateMaxHostExternalSettingsLen = 16000 89 90 // NegotiateMaxSiaPubkeySize defines the maximum size that a SiaPubkey is 91 // allowed to be when being sent over the wire during negotiation. 92 NegotiateMaxSiaPubkeySize = 1e3 93 94 // NegotiateMaxTransactionSignatureSize defines the maximum size that a 95 // transaction signature is allowed to be when being sent over the wire 96 // during negoitation. 97 NegotiateMaxTransactionSignatureSize = 2e3 98 99 // NegotiateMaxTransactionSignaturesSize defines the maximum size that a 100 // transaction signature slice is allowed to be when being sent over the 101 // wire during negoitation. 102 NegotiateMaxTransactionSignaturesSize = 5e3 103 ) 104 105 var ( 106 // ActionDelete is the specifier for a RevisionAction that deletes a 107 // sector. 108 ActionDelete = types.Specifier{'D', 'e', 'l', 'e', 't', 'e'} 109 110 // ActionInsert is the specifier for a RevisionAction that inserts a 111 // sector. 112 ActionInsert = types.Specifier{'I', 'n', 's', 'e', 'r', 't'} 113 114 // ActionModify is the specifier for a RevisionAction that modifies sector 115 // data. 116 ActionModify = types.Specifier{'M', 'o', 'd', 'i', 'f', 'y'} 117 118 // ErrAnnNotAnnouncement indicates that the provided host announcement does 119 // not use a recognized specifier, indicating that it's either not a host 120 // announcement or it's not a recognized version of a host announcement. 121 ErrAnnNotAnnouncement = errors.New("provided data does not form a recognized host announcement") 122 123 // ErrAnnUnrecognizedSignature is returned when the signature in a host 124 // announcement is not a type of signature that is recognized. 125 ErrAnnUnrecognizedSignature = errors.New("the signature provided in the host announcement is not recognized") 126 127 // ErrRevisionCoveredFields is returned if there is a covered fields object 128 // in a transaction signature which has the 'WholeTransaction' field set to 129 // true, meaning that miner fees cannot be added to the transaction without 130 // invalidating the signature. 131 ErrRevisionCoveredFields = errors.New("file contract revision transaction signature does not allow miner fees to be added") 132 133 // ErrRevisionSigCount is returned when a file contract revision has the 134 // wrong number of transaction signatures. 135 ErrRevisionSigCount = errors.New("file contract revision has the wrong number of transaction signatures") 136 137 // ErrStopResponse is the error returned by ReadNegotiationAcceptance when 138 // it reads the StopResponse string. 139 ErrStopResponse = errors.New("sender wishes to stop communicating") 140 141 // PrefixHostAnnouncement is used to indicate that a transaction's 142 // Arbitrary Data field contains a host announcement. The encoded 143 // announcement will follow this prefix. 144 PrefixHostAnnouncement = types.Specifier{'H', 'o', 's', 't', 'A', 'n', 'n', 'o', 'u', 'n', 'c', 'e', 'm', 'e', 'n', 't'} 145 146 // RPCDownload is the specifier for downloading a file from a host. 147 RPCDownload = types.Specifier{'D', 'o', 'w', 'n', 'l', 'o', 'a', 'd', 2} 148 149 // RPCFormContract is the specifier for forming a contract with a host. 150 RPCFormContract = types.Specifier{'F', 'o', 'r', 'm', 'C', 'o', 'n', 't', 'r', 'a', 'c', 't', 2} 151 152 // RPCRenewContract is the specifier to renewing an existing contract. 153 RPCRenewContract = types.Specifier{'R', 'e', 'n', 'e', 'w', 'C', 'o', 'n', 't', 'r', 'a', 'c', 't', 2} 154 155 // RPCReviseContract is the specifier for revising an existing file 156 // contract. 157 RPCReviseContract = types.Specifier{'R', 'e', 'v', 'i', 's', 'e', 'C', 'o', 'n', 't', 'r', 'a', 'c', 't', 2} 158 159 // RPCRecentRevision is the specifier for getting the most recent file 160 // contract revision for a given file contract. 161 RPCRecentRevision = types.Specifier{'R', 'e', 'c', 'e', 'n', 't', 'R', 'e', 'v', 'i', 's', 'i', 'o', 'n', 2} 162 163 // RPCSettings is the specifier for requesting settings from the host. 164 RPCSettings = types.Specifier{'S', 'e', 't', 't', 'i', 'n', 'g', 's', 2} 165 166 // SectorSize defines how large a sector should be in bytes. The sector 167 // size needs to be a power of two to be compatible with package 168 // merkletree. 4MB has been chosen for the live network because large 169 // sectors significantly reduce the tracking overhead experienced by the 170 // renter and the host. 171 SectorSize = func() uint64 { 172 if build.Release == "dev" { 173 return 1 << 20 // 1 MiB 174 } 175 if build.Release == "standard" { 176 return 1 << 22 // 4 MiB 177 } 178 if build.Release == "testing" { 179 return 1 << 12 // 4 KiB 180 } 181 panic("unrecognized release constant in host - sectorSize") 182 }() 183 ) 184 185 type ( 186 // A DownloadAction is a description of a download that the renter would 187 // like to make. The MerkleRoot indicates the root of the sector, the 188 // offset indicates what portion of the sector is being downloaded, and the 189 // length indicates how many bytes should be grabbed starting from the 190 // offset. 191 DownloadAction struct { 192 MerkleRoot crypto.Hash 193 Offset uint64 194 Length uint64 195 } 196 197 // HostAnnouncement is an announcement by the host that appears in the 198 // blockchain. 'Specifier' is always 'PrefixHostAnnouncement'. The 199 // announcement is always followed by a signature from the public key of 200 // the whole announcement. 201 HostAnnouncement struct { 202 Specifier types.Specifier 203 NetAddress NetAddress 204 PublicKey types.SiaPublicKey 205 } 206 207 // HostExternalSettings are the parameters advertised by the host. These 208 // are the values that the renter will request from the host in order to 209 // build its database. 210 HostExternalSettings struct { 211 // MaxBatchSize indicates the maximum size in bytes that a batch is 212 // allowed to be. A batch is an array of revision actions, each 213 // revision action can have a different number of bytes, depending on 214 // the action, so the number of revision actions allowed depends on the 215 // sizes of each. 216 AcceptingContracts bool `json:"acceptingcontracts"` 217 MaxDownloadBatchSize uint64 `json:"maxdownloadbatchsize"` 218 MaxReviseBatchSize uint64 `json:"maxrevisebatchsize"` 219 MaxDuration types.BlockHeight `json:"maxduration"` 220 NetAddress NetAddress `json:"netaddress"` 221 RemainingStorage uint64 `json:"remainingstorage"` 222 SectorSize uint64 `json:"sectorsize"` 223 TotalStorage uint64 `json:"totalstorage"` 224 UnlockHash types.UnlockHash `json:"unlockhash"` 225 WindowSize types.BlockHeight `json:"windowsize"` 226 227 // Collateral is the amount of collateral that the host will put up for 228 // storage in 'bytes per block', as an assurance to the renter that the 229 // host really is committed to keeping the file. But, because the file 230 // contract is created with no data available, this does leave the host 231 // exposed to an attack by a wealthy renter whereby the renter causes 232 // the host to lockup in-advance a bunch of funds that the renter then 233 // never uses, meaning the host will not have collateral for other 234 // clients. 235 // 236 // MaxCollateral indicates the maximum number of coins that a host is 237 // willing to put into a file contract. 238 Collateral types.Currency `json:"collateral"` 239 MaxCollateral types.Currency `json:"maxcollateral"` 240 241 // ContractPrice is the number of coins that the renter needs to pay to 242 // the host just to open a file contract with them. Generally, the 243 // price is only to cover the siacoin fees that the host will suffer 244 // when submitting the file contract revision and storage proof to the 245 // blockchain. 246 // 247 // The storage price is the cost per-byte-per-block in hastings of 248 // storing data on the host. 249 // 250 // 'Download' bandwidth price is the cost per byte of downloading data 251 // from the host. 252 // 253 // 'Upload' bandwidth price is the cost per byte of uploading data to 254 // the host. 255 ContractPrice types.Currency `json:"contractprice"` 256 DownloadBandwidthPrice types.Currency `json:"downloadbandwidthprice"` 257 StoragePrice types.Currency `json:"storageprice"` 258 UploadBandwidthPrice types.Currency `json:"uploadbandwidthprice"` 259 260 // Because the host has a public key, and settings are signed, and 261 // because settings may be MITM'd, settings need a revision number so 262 // that a renter can compare multiple sets of settings and determine 263 // which is the most recent. 264 RevisionNumber uint64 `json:"revisionnumber"` 265 Version string `json:"version"` 266 } 267 268 // A RevisionAction is a description of an edit to be performed on a file 269 // contract. Three types are allowed, 'ActionDelecte', 'ActionInsert', and 270 // 'ActionModify'. ActionDelete just takes a sector index, indicating which 271 // sector is going to be deleted. ActionInsert takes a sector index, and a 272 // full sector of data, indicating that a sector at the index should be 273 // inserted with the provided data. 'Modify' revises the sector at the 274 // given index, rewriting it with the provided data starting from the 275 // 'offset' within the sector. 276 // 277 // Modify could be simulated with an insert and a delete, however an insert 278 // requires a full sector to be uploaded, and a modify can be just a few 279 // kb, which can be significantly faster. 280 RevisionAction struct { 281 Type types.Specifier 282 SectorIndex uint64 283 Offset uint64 284 Data []byte 285 } 286 ) 287 288 // ReadNegotiationAcceptance reads an accept/reject response from r (usually a 289 // net.Conn). If the response is not AcceptResponse, ReadNegotiationAcceptance 290 // returns the response as an error. If the response is StopResponse, 291 // ErrStopResponse is returned, allowing for direct error comparison. 292 // 293 // Note that since errors returned by ReadNegotiationAcceptance are newly 294 // allocated, they cannot be compared to other errors in the traditional 295 // fashion. 296 func ReadNegotiationAcceptance(r io.Reader) error { 297 var resp string 298 err := encoding.ReadObject(r, &resp, NegotiateMaxErrorSize) 299 if err != nil { 300 return err 301 } 302 switch resp { 303 case AcceptResponse: 304 return nil 305 case StopResponse: 306 return ErrStopResponse 307 default: 308 return errors.New(resp) 309 } 310 } 311 312 // WriteNegotiationAcceptance writes the 'accept' response to w (usually a 313 // net.Conn). 314 func WriteNegotiationAcceptance(w io.Writer) error { 315 return encoding.WriteObject(w, AcceptResponse) 316 } 317 318 // WriteNegotiationRejection will write a rejection response to w (usually a 319 // net.Conn) and return the input error. If the write fails, the write error 320 // is joined with the input error. 321 func WriteNegotiationRejection(w io.Writer, err error) error { 322 writeErr := encoding.WriteObject(w, err.Error()) 323 if writeErr != nil { 324 return build.JoinErrors([]error{err, writeErr}, "; ") 325 } 326 return err 327 } 328 329 // WriteNegotiationStop writes the 'stop' response to w (usually a 330 // net.Conn). 331 func WriteNegotiationStop(w io.Writer) error { 332 return encoding.WriteObject(w, StopResponse) 333 } 334 335 // CreateAnnouncement will take a host announcement and encode it, returning 336 // the exact []byte that should be added to the arbitrary data of a 337 // transaction. 338 func CreateAnnouncement(addr NetAddress, pk types.SiaPublicKey, sk crypto.SecretKey) (signedAnnouncement []byte, err error) { 339 if err := addr.IsValid(); err != nil { 340 return nil, err 341 } 342 343 // Create the HostAnnouncement and marshal it. 344 annBytes := encoding.Marshal(HostAnnouncement{ 345 Specifier: PrefixHostAnnouncement, 346 NetAddress: addr, 347 PublicKey: pk, 348 }) 349 350 // Create a signature for the announcement. 351 annHash := crypto.HashBytes(annBytes) 352 sig, err := crypto.SignHash(annHash, sk) 353 if err != nil { 354 return nil, err 355 } 356 // Return the signed announcement. 357 return append(annBytes, sig[:]...), nil 358 } 359 360 // DecodeAnnouncement decodes announcement bytes into a host announcement, 361 // verifying the prefix and the signature. 362 func DecodeAnnouncement(fullAnnouncement []byte) (na NetAddress, spk types.SiaPublicKey, err error) { 363 // Read the first part of the announcement to get the intended host 364 // announcement. 365 var ha HostAnnouncement 366 dec := encoding.NewDecoder(bytes.NewReader(fullAnnouncement)) 367 err = dec.Decode(&ha) 368 if err != nil { 369 return "", types.SiaPublicKey{}, err 370 } 371 372 // Check that the announcement was registered as a host announcement. 373 if ha.Specifier != PrefixHostAnnouncement { 374 return "", types.SiaPublicKey{}, ErrAnnNotAnnouncement 375 } 376 // Check that the public key is a recognized type of public key. 377 if ha.PublicKey.Algorithm != types.SignatureEd25519 { 378 return "", types.SiaPublicKey{}, ErrAnnUnrecognizedSignature 379 } 380 381 // Read the signature out of the reader. 382 var sig crypto.Signature 383 err = dec.Decode(&sig) 384 if err != nil { 385 return "", types.SiaPublicKey{}, err 386 } 387 // Verify the signature. 388 var pk crypto.PublicKey 389 copy(pk[:], ha.PublicKey.Key) 390 annHash := crypto.HashObject(ha) 391 err = crypto.VerifyHash(annHash, pk, sig) 392 if err != nil { 393 return "", types.SiaPublicKey{}, err 394 } 395 return ha.NetAddress, ha.PublicKey, nil 396 } 397 398 // VerifyFileContractRevisionTransactionSignatures checks that the signatures 399 // on a file contract revision are valid and cover the right fields. 400 func VerifyFileContractRevisionTransactionSignatures(fcr types.FileContractRevision, tsigs []types.TransactionSignature, height types.BlockHeight) error { 401 if len(tsigs) != 2 { 402 return ErrRevisionSigCount 403 } 404 for _, tsig := range tsigs { 405 // The transaction needs to be malleable so that miner fees can be 406 // added. If the whole transaction is covered, it is doomed to have no 407 // fees. 408 if tsig.CoveredFields.WholeTransaction { 409 return ErrRevisionCoveredFields 410 } 411 } 412 txn := types.Transaction{ 413 FileContractRevisions: []types.FileContractRevision{fcr}, 414 TransactionSignatures: tsigs, 415 } 416 // Check that the signatures verify. This will also check that the covered 417 // fields object is not over-aggressive, because if the object is pointing 418 // to elements that haven't been added to the transaction, verification 419 // will fail. 420 return txn.StandaloneValid(height) 421 }