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