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