github.com/Synthesix/Sia@v1.3.3-0.20180413141344-f863baeed3ca/modules/renter/proto/contract.go (about) 1 package proto 2 3 import ( 4 "encoding/json" 5 "errors" 6 "io" 7 "os" 8 "path/filepath" 9 "sync" 10 11 "github.com/Synthesix/Sia/crypto" 12 "github.com/Synthesix/Sia/encoding" 13 "github.com/Synthesix/Sia/modules" 14 "github.com/Synthesix/Sia/types" 15 "github.com/NebulousLabs/writeaheadlog" 16 ) 17 18 const ( 19 // contractHeaderSize is the maximum amount of space that the non-Merkle-root 20 // portion of a contract can consume. 21 contractHeaderSize = writeaheadlog.MaxPayloadSize // TODO: test this 22 23 updateNameSetHeader = "setHeader" 24 updateNameSetRoot = "setRoot" 25 ) 26 27 type updateSetHeader struct { 28 ID types.FileContractID 29 Header contractHeader 30 } 31 32 type updateSetRoot struct { 33 ID types.FileContractID 34 Root crypto.Hash 35 Index int 36 } 37 38 type contractHeader struct { 39 // transaction is the signed transaction containing the most recent 40 // revision of the file contract. 41 Transaction types.Transaction 42 43 // secretKey is the key used by the renter to sign the file contract 44 // transaction. 45 SecretKey crypto.SecretKey 46 47 // Same as modules.RenterContract. 48 StartHeight types.BlockHeight 49 DownloadSpending types.Currency 50 StorageSpending types.Currency 51 UploadSpending types.Currency 52 TotalCost types.Currency 53 ContractFee types.Currency 54 TxnFee types.Currency 55 SiafundFee types.Currency 56 } 57 58 // validate returns an error if the contractHeader is invalid. 59 func (h *contractHeader) validate() error { 60 if len(h.Transaction.FileContractRevisions) > 0 && 61 len(h.Transaction.FileContractRevisions[0].NewValidProofOutputs) > 0 && 62 len(h.Transaction.FileContractRevisions[0].UnlockConditions.PublicKeys) == 2 { 63 return nil 64 } 65 return errors.New("invalid contract") 66 } 67 68 func (h *contractHeader) copyTransaction() (txn types.Transaction) { 69 encoding.Unmarshal(encoding.Marshal(h.Transaction), &txn) 70 return 71 } 72 73 func (h *contractHeader) LastRevision() types.FileContractRevision { 74 return h.Transaction.FileContractRevisions[0] 75 } 76 77 func (h *contractHeader) ID() types.FileContractID { 78 return h.LastRevision().ParentID 79 } 80 81 func (h *contractHeader) HostPublicKey() types.SiaPublicKey { 82 return h.LastRevision().UnlockConditions.PublicKeys[1] 83 } 84 85 func (h *contractHeader) RenterFunds() types.Currency { 86 return h.LastRevision().NewValidProofOutputs[0].Value 87 } 88 89 func (h *contractHeader) EndHeight() types.BlockHeight { 90 return h.LastRevision().NewWindowStart 91 } 92 93 // A SafeContract contains the most recent revision transaction negotiated 94 // with a host, and the secret key used to sign it. 95 type SafeContract struct { 96 headerMu sync.Mutex 97 header contractHeader 98 99 // merkleRoots are the Merkle roots of each sector stored on the host that 100 // relate to this contract. 101 merkleRoots []crypto.Hash 102 103 // unappliedTxns are the transactions that were written to the WAL but not 104 // applied to the contract file. 105 unappliedTxns []*writeaheadlog.Transaction 106 107 f *os.File // TODO: use a dependency for this 108 wal *writeaheadlog.WAL 109 mu sync.Mutex 110 } 111 112 // Metadata returns the metadata of a renter contract 113 func (c *SafeContract) Metadata() modules.RenterContract { 114 c.headerMu.Lock() 115 defer c.headerMu.Unlock() 116 h := c.header 117 return modules.RenterContract{ 118 ID: h.ID(), 119 Transaction: h.copyTransaction(), 120 HostPublicKey: h.HostPublicKey(), 121 StartHeight: h.StartHeight, 122 EndHeight: h.EndHeight(), 123 RenterFunds: h.RenterFunds(), 124 DownloadSpending: h.DownloadSpending, 125 StorageSpending: h.StorageSpending, 126 UploadSpending: h.UploadSpending, 127 TotalCost: h.TotalCost, 128 ContractFee: h.ContractFee, 129 TxnFee: h.TxnFee, 130 SiafundFee: h.SiafundFee, 131 } 132 } 133 134 func (c *SafeContract) makeUpdateSetHeader(h contractHeader) writeaheadlog.Update { 135 c.headerMu.Lock() 136 id := c.header.ID() 137 c.headerMu.Unlock() 138 return writeaheadlog.Update{ 139 Name: updateNameSetHeader, 140 Instructions: encoding.Marshal(updateSetHeader{ 141 ID: id, 142 Header: h, 143 }), 144 } 145 } 146 147 func (c *SafeContract) makeUpdateSetRoot(root crypto.Hash, index int) writeaheadlog.Update { 148 c.headerMu.Lock() 149 id := c.header.ID() 150 c.headerMu.Unlock() 151 return writeaheadlog.Update{ 152 Name: updateNameSetRoot, 153 Instructions: encoding.Marshal(updateSetRoot{ 154 ID: id, 155 Root: root, 156 Index: index, 157 }), 158 } 159 } 160 161 func (c *SafeContract) applySetHeader(h contractHeader) error { 162 headerBytes := make([]byte, contractHeaderSize) 163 copy(headerBytes, encoding.Marshal(h)) 164 if _, err := c.f.WriteAt(headerBytes, 0); err != nil { 165 return err 166 } 167 c.headerMu.Lock() 168 c.header = h 169 c.headerMu.Unlock() 170 return nil 171 } 172 173 func (c *SafeContract) applySetRoot(root crypto.Hash, index int) error { 174 rootOffset := contractHeaderSize + crypto.HashSize*int64(index) 175 if _, err := c.f.WriteAt(root[:], rootOffset); err != nil { 176 return err 177 } 178 if len(c.merkleRoots) <= index { 179 c.merkleRoots = append(c.merkleRoots, make([]crypto.Hash, 1+index-len(c.merkleRoots))...) 180 } 181 c.merkleRoots[index] = root 182 return nil 183 } 184 185 func (c *SafeContract) recordUploadIntent(rev types.FileContractRevision, root crypto.Hash, storageCost, bandwidthCost types.Currency) (*writeaheadlog.Transaction, error) { 186 // construct new header 187 // NOTE: this header will not include the host signature 188 c.headerMu.Lock() 189 newHeader := c.header 190 c.headerMu.Unlock() 191 newHeader.Transaction.FileContractRevisions = []types.FileContractRevision{rev} 192 newHeader.StorageSpending = newHeader.StorageSpending.Add(storageCost) 193 newHeader.UploadSpending = newHeader.UploadSpending.Add(bandwidthCost) 194 195 rootIndex := len(c.merkleRoots) 196 t, err := c.wal.NewTransaction([]writeaheadlog.Update{ 197 c.makeUpdateSetHeader(newHeader), 198 c.makeUpdateSetRoot(root, rootIndex), 199 }) 200 if err != nil { 201 return nil, err 202 } 203 if err := <-t.SignalSetupComplete(); err != nil { 204 return nil, err 205 } 206 c.unappliedTxns = append(c.unappliedTxns, t) 207 return t, nil 208 } 209 210 func (c *SafeContract) commitUpload(t *writeaheadlog.Transaction, signedTxn types.Transaction, root crypto.Hash, storageCost, bandwidthCost types.Currency) error { 211 // construct new header 212 c.headerMu.Lock() 213 newHeader := c.header 214 c.headerMu.Unlock() 215 newHeader.Transaction = signedTxn 216 newHeader.StorageSpending = newHeader.StorageSpending.Add(storageCost) 217 newHeader.UploadSpending = newHeader.UploadSpending.Add(bandwidthCost) 218 219 rootIndex := len(c.merkleRoots) 220 if err := c.applySetHeader(newHeader); err != nil { 221 return err 222 } 223 if err := c.applySetRoot(root, rootIndex); err != nil { 224 return err 225 } 226 if err := c.f.Sync(); err != nil { 227 return err 228 } 229 if err := t.SignalUpdatesApplied(); err != nil { 230 return err 231 } 232 c.unappliedTxns = nil 233 return nil 234 } 235 236 func (c *SafeContract) recordDownloadIntent(rev types.FileContractRevision, bandwidthCost types.Currency) (*writeaheadlog.Transaction, error) { 237 // construct new header 238 // NOTE: this header will not include the host signature 239 c.headerMu.Lock() 240 newHeader := c.header 241 c.headerMu.Unlock() 242 newHeader.Transaction.FileContractRevisions = []types.FileContractRevision{rev} 243 newHeader.DownloadSpending = newHeader.DownloadSpending.Add(bandwidthCost) 244 245 t, err := c.wal.NewTransaction([]writeaheadlog.Update{ 246 c.makeUpdateSetHeader(newHeader), 247 }) 248 if err != nil { 249 return nil, err 250 } 251 if err := <-t.SignalSetupComplete(); err != nil { 252 return nil, err 253 } 254 c.unappliedTxns = append(c.unappliedTxns, t) 255 return t, nil 256 } 257 258 func (c *SafeContract) commitDownload(t *writeaheadlog.Transaction, signedTxn types.Transaction, bandwidthCost types.Currency) error { 259 // construct new header 260 c.headerMu.Lock() 261 newHeader := c.header 262 c.headerMu.Unlock() 263 newHeader.Transaction = signedTxn 264 newHeader.DownloadSpending = newHeader.DownloadSpending.Add(bandwidthCost) 265 266 if err := c.applySetHeader(newHeader); err != nil { 267 return err 268 } 269 if err := c.f.Sync(); err != nil { 270 return err 271 } 272 if err := t.SignalUpdatesApplied(); err != nil { 273 return err 274 } 275 c.unappliedTxns = nil 276 return nil 277 } 278 279 // commitTxns commits the unapplied transactions to the contract file and marks 280 // the transactions as applied. 281 func (c *SafeContract) commitTxns() error { 282 for _, t := range c.unappliedTxns { 283 for _, update := range t.Updates { 284 switch update.Name { 285 case updateNameSetHeader: 286 var u updateSetHeader 287 if err := encoding.Unmarshal(update.Instructions, &u); err != nil { 288 return err 289 } 290 if err := c.applySetHeader(u.Header); err != nil { 291 return err 292 } 293 case updateNameSetRoot: 294 var u updateSetRoot 295 if err := encoding.Unmarshal(update.Instructions, &u); err != nil { 296 return err 297 } 298 if err := c.applySetRoot(u.Root, u.Index); err != nil { 299 return err 300 } 301 } 302 } 303 if err := c.f.Sync(); err != nil { 304 return err 305 } 306 if err := t.SignalUpdatesApplied(); err != nil { 307 return err 308 } 309 } 310 c.unappliedTxns = nil 311 return nil 312 } 313 314 // unappliedHeader returns the most recent header contained within the unapplied 315 // transactions relevant to the contract. 316 func (c *SafeContract) unappliedHeader() (h contractHeader) { 317 for _, t := range c.unappliedTxns { 318 for _, update := range t.Updates { 319 if update.Name == updateNameSetHeader { 320 var u updateSetHeader 321 if err := encoding.Unmarshal(update.Instructions, &u); err != nil { 322 continue 323 } 324 h = u.Header 325 } 326 } 327 } 328 return 329 } 330 331 func (cs *ContractSet) managedInsertContract(h contractHeader, roots []crypto.Hash) (modules.RenterContract, error) { 332 if err := h.validate(); err != nil { 333 return modules.RenterContract{}, err 334 } 335 f, err := os.Create(filepath.Join(cs.dir, h.ID().String()+contractExtension)) 336 if err != nil { 337 return modules.RenterContract{}, err 338 } 339 // preallocate space for header + roots 340 if err := f.Truncate(contractHeaderSize + crypto.HashSize*int64(len(roots))); err != nil { 341 return modules.RenterContract{}, err 342 } 343 // write header 344 if _, err := f.WriteAt(encoding.Marshal(h), 0); err != nil { 345 return modules.RenterContract{}, err 346 } 347 // write roots 348 for i, root := range roots { 349 if _, err := f.WriteAt(root[:], contractHeaderSize+crypto.HashSize*int64(i)); err != nil { 350 return modules.RenterContract{}, err 351 } 352 } 353 if err := f.Sync(); err != nil { 354 return modules.RenterContract{}, err 355 } 356 sc := &SafeContract{ 357 header: h, 358 merkleRoots: roots, 359 f: f, 360 wal: cs.wal, 361 } 362 cs.mu.Lock() 363 cs.contracts[h.ID()] = sc 364 cs.mu.Unlock() 365 return sc.Metadata(), nil 366 } 367 368 func (cs *ContractSet) loadSafeContract(filename string, walTxns []*writeaheadlog.Transaction) error { 369 f, err := os.OpenFile(filename, os.O_RDWR, 0600) 370 if err != nil { 371 return err 372 } 373 // read header 374 var header contractHeader 375 if err := encoding.NewDecoder(f).Decode(&header); err != nil { 376 return err 377 } else if err := header.validate(); err != nil { 378 return err 379 } 380 // read merkleRoots 381 var merkleRoots []crypto.Hash 382 if _, err := f.Seek(contractHeaderSize, io.SeekStart); err != nil { 383 return err 384 } 385 for { 386 var root crypto.Hash 387 if _, err := io.ReadFull(f, root[:]); err == io.EOF { 388 break 389 } else if err != nil { 390 return err 391 } 392 merkleRoots = append(merkleRoots, root) 393 } 394 // add relevant unapplied transactions 395 var unappliedTxns []*writeaheadlog.Transaction 396 for _, t := range walTxns { 397 // NOTE: we assume here that if any of the updates apply to the 398 // contract, the whole transaction applies to the contract. 399 if len(t.Updates) == 0 { 400 continue 401 } 402 var id types.FileContractID 403 switch update := t.Updates[0]; update.Name { 404 case updateNameSetHeader: 405 var u updateSetHeader 406 if err := encoding.Unmarshal(update.Instructions, &u); err != nil { 407 return err 408 } 409 id = u.ID 410 case updateNameSetRoot: 411 var u updateSetRoot 412 if err := encoding.Unmarshal(update.Instructions, &u); err != nil { 413 return err 414 } 415 id = u.ID 416 } 417 if id == header.ID() { 418 unappliedTxns = append(unappliedTxns, t) 419 } 420 } 421 // add to set 422 cs.contracts[header.ID()] = &SafeContract{ 423 header: header, 424 merkleRoots: merkleRoots, 425 unappliedTxns: unappliedTxns, 426 f: f, 427 wal: cs.wal, 428 } 429 return nil 430 } 431 432 // ConvertV130Contract creates a contract file for a v130 contract. 433 func (cs *ContractSet) ConvertV130Contract(c V130Contract, cr V130CachedRevision) error { 434 m, err := cs.managedInsertContract(contractHeader{ 435 Transaction: c.LastRevisionTxn, 436 SecretKey: c.SecretKey, 437 StartHeight: c.StartHeight, 438 DownloadSpending: c.DownloadSpending, 439 StorageSpending: c.StorageSpending, 440 UploadSpending: c.UploadSpending, 441 TotalCost: c.TotalCost, 442 ContractFee: c.ContractFee, 443 TxnFee: c.TxnFee, 444 SiafundFee: c.SiafundFee, 445 }, c.MerkleRoots) 446 if err != nil { 447 return err 448 } 449 // if there is a cached revision, store it as an unapplied WAL transaction 450 if cr.Revision.NewRevisionNumber != 0 { 451 sc, ok := cs.Acquire(m.ID) 452 if !ok { 453 return errors.New("contract set is missing contract that was just added") 454 } 455 defer cs.Return(sc) 456 if len(cr.MerkleRoots) == len(sc.merkleRoots)+1 { 457 root := cr.MerkleRoots[len(cr.MerkleRoots)-1] 458 _, err = sc.recordUploadIntent(cr.Revision, root, types.ZeroCurrency, types.ZeroCurrency) 459 } else { 460 _, err = sc.recordDownloadIntent(cr.Revision, types.ZeroCurrency) 461 } 462 if err != nil { 463 return err 464 } 465 } 466 return nil 467 } 468 469 // A V130Contract specifies the v130 contract format. 470 type V130Contract struct { 471 HostPublicKey types.SiaPublicKey `json:"hostpublickey"` 472 ID types.FileContractID `json:"id"` 473 LastRevision types.FileContractRevision `json:"lastrevision"` 474 LastRevisionTxn types.Transaction `json:"lastrevisiontxn"` 475 MerkleRoots MerkleRootSet `json:"merkleroots"` 476 SecretKey crypto.SecretKey `json:"secretkey"` 477 StartHeight types.BlockHeight `json:"startheight"` 478 DownloadSpending types.Currency `json:"downloadspending"` 479 StorageSpending types.Currency `json:"storagespending"` 480 UploadSpending types.Currency `json:"uploadspending"` 481 TotalCost types.Currency `json:"totalcost"` 482 ContractFee types.Currency `json:"contractfee"` 483 TxnFee types.Currency `json:"txnfee"` 484 SiafundFee types.Currency `json:"siafundfee"` 485 } 486 487 // EndHeight returns the height at which the host is no longer obligated to 488 // store contract data. 489 func (c *V130Contract) EndHeight() types.BlockHeight { 490 return c.LastRevision.NewWindowStart 491 } 492 493 // RenterFunds returns the funds remaining in the contract's Renter payout as 494 // of the most recent revision. 495 func (c *V130Contract) RenterFunds() types.Currency { 496 if len(c.LastRevision.NewValidProofOutputs) < 2 { 497 return types.ZeroCurrency 498 } 499 return c.LastRevision.NewValidProofOutputs[0].Value 500 } 501 502 // A V130CachedRevision contains changes that would be applied to a 503 // RenterContract if a contract revision succeeded. 504 type V130CachedRevision struct { 505 Revision types.FileContractRevision `json:"revision"` 506 MerkleRoots modules.MerkleRootSet `json:"merkleroots"` 507 } 508 509 // MerkleRootSet is a set of Merkle roots, and gets encoded more efficiently. 510 type MerkleRootSet []crypto.Hash 511 512 // MarshalJSON defines a JSON encoding for a MerkleRootSet. 513 func (mrs MerkleRootSet) MarshalJSON() ([]byte, error) { 514 // Copy the whole array into a giant byte slice and then encode that. 515 fullBytes := make([]byte, crypto.HashSize*len(mrs)) 516 for i := range mrs { 517 copy(fullBytes[i*crypto.HashSize:(i+1)*crypto.HashSize], mrs[i][:]) 518 } 519 return json.Marshal(fullBytes) 520 } 521 522 // UnmarshalJSON attempts to decode a MerkleRootSet, falling back on the legacy 523 // decoding of a []crypto.Hash if that fails. 524 func (mrs *MerkleRootSet) UnmarshalJSON(b []byte) error { 525 // Decode the giant byte slice, and then split it into separate arrays. 526 var fullBytes []byte 527 err := json.Unmarshal(b, &fullBytes) 528 if err != nil { 529 // Encoding the byte slice has failed, try decoding it as a []crypto.Hash. 530 var hashes []crypto.Hash 531 err := json.Unmarshal(b, &hashes) 532 if err != nil { 533 return err 534 } 535 *mrs = MerkleRootSet(hashes) 536 return nil 537 } 538 539 umrs := make(MerkleRootSet, len(fullBytes)/32) 540 for i := range umrs { 541 copy(umrs[i][:], fullBytes[i*crypto.HashSize:(i+1)*crypto.HashSize]) 542 } 543 *mrs = umrs 544 return nil 545 }