gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/proto/contract.go (about) 1 package proto 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "reflect" 11 "sync" 12 13 "gitlab.com/NebulousLabs/errors" 14 "gitlab.com/NebulousLabs/writeaheadlog" 15 16 "gitlab.com/NebulousLabs/encoding" 17 "gitlab.com/SkynetLabs/skyd/build" 18 "gitlab.com/SkynetLabs/skyd/skymodules" 19 "go.sia.tech/siad/crypto" 20 "go.sia.tech/siad/modules" 21 "go.sia.tech/siad/types" 22 ) 23 24 const ( 25 updateNameInsertContract = "insertContract" 26 updateNameSetHeader = "setHeader" 27 updateNameSetRoot = "setRoot" 28 29 // decodeMaxSizeMultiplier is multiplied with the size of an encoded object 30 // to allocated a bit of extra space for decoding. 31 decodeMaxSizeMultiplier = 3 32 ) 33 34 // updateInsertContract is an update that inserts a contract into the 35 // contractset with the given header and roots. 36 type updateInsertContract struct { 37 Header contractHeader 38 Roots []crypto.Hash 39 } 40 41 // updateSetHeader is an update that updates the header of the filecontract with 42 // the given id. 43 type updateSetHeader struct { 44 ID types.FileContractID 45 Header contractHeader 46 } 47 48 // updateSetRoot is an update which updates the sector root at the given index 49 // of a filecontract with the specified id. 50 type updateSetRoot struct { 51 ID types.FileContractID 52 Root crypto.Hash 53 Index int 54 } 55 56 // contractHeader holds all the information about a contract apart from the 57 // sector roots themselves. 58 type contractHeader struct { 59 // transaction is the signed transaction containing the most recent 60 // revision of the file contract. 61 Transaction types.Transaction 62 63 // secretKey is the key used by the renter to sign the file contract 64 // transaction. 65 SecretKey crypto.SecretKey 66 67 // Same as skymodules.RenterContract. 68 StartHeight types.BlockHeight 69 DownloadSpending types.Currency 70 FundAccountSpending types.Currency 71 MaintenanceSpending skymodules.MaintenanceSpending 72 StorageSpending types.Currency 73 UploadSpending types.Currency 74 TotalCost types.Currency 75 ContractFee types.Currency 76 TxnFee types.Currency 77 SiafundFee types.Currency 78 Utility skymodules.ContractUtility 79 } 80 81 // rootUpdate is a helper type that specifies an update to a root. 82 type rootUpdate struct { 83 // appended means the root didn't exist before and will be appended to the 84 // contract. Otherwise the update updates an existing root. This field is 85 // useful in combination with trim because a new root that is also trimmed 86 // doesn't require an update on disk since the root didn't exist before 87 // anyway. 88 appended bool 89 // trim indicates that the root is supposed to be trimmed. 90 trim bool 91 // root is the new value of the root. This may be the empty hash if the root 92 // is meant to be trimmed. 93 root crypto.Hash 94 } 95 96 // newRootUpdateTrimRoot creates a trim update. 97 func newRootUpdateTrimRoot() rootUpdate { 98 return rootUpdate{trim: true} 99 } 100 101 // newRootUpdateAppendRoot creates an append update. 102 func newRootUpdateAppendRoot(root crypto.Hash) rootUpdate { 103 return rootUpdate{root: root, appended: true} 104 } 105 106 // newRootUpdateUpdateRoot creates a regular update. 107 func newRootUpdateUpdateRoot(root crypto.Hash) rootUpdate { 108 return rootUpdate{root: root, appended: false} 109 } 110 111 // validate returns an error if the contractHeader is invalid. 112 func (h *contractHeader) validate() error { 113 if len(h.Transaction.FileContractRevisions) == 0 { 114 return errors.New("no file contract revisions") 115 } 116 if len(h.Transaction.FileContractRevisions[0].NewValidProofOutputs) == 0 { 117 return errors.New("not enough valid proof outputs") 118 } 119 if len(h.Transaction.FileContractRevisions[0].UnlockConditions.PublicKeys) != 2 { 120 return errors.New("wrong number of pubkeys") 121 } 122 return nil 123 } 124 125 // copyTransaction creates a deep copy of the txn struct. 126 func (h *contractHeader) copyTransaction() (txn types.Transaction) { 127 encoding.Unmarshal(encoding.Marshal(h.Transaction), &txn) 128 return 129 } 130 131 // LastRevision returns the last revision of the contract. 132 func (h *contractHeader) LastRevision() types.FileContractRevision { 133 return h.Transaction.FileContractRevisions[0] 134 } 135 136 // ID returns the contract's ID. 137 func (h *contractHeader) ID() types.FileContractID { 138 return h.LastRevision().ID() 139 } 140 141 // HostPublicKey returns the host's public key from the last contract revision. 142 func (h *contractHeader) HostPublicKey() types.SiaPublicKey { 143 return h.LastRevision().HostPublicKey() 144 } 145 146 // RenterFunds returns the remaining renter funds as per the last contract 147 // revision. 148 func (h *contractHeader) RenterFunds() types.Currency { 149 return h.LastRevision().ValidRenterPayout() 150 } 151 152 // EndHeight returns the block height of the last constract revision. 153 func (h *contractHeader) EndHeight() types.BlockHeight { 154 return h.LastRevision().EndHeight() 155 } 156 157 // unappliedWalTxn is a wrapper around writeaheadlog.Transaction that guarantees 158 // we only call `SignalUpdatesApplied` once. 159 type unappliedWalTxn struct { 160 err error 161 once sync.Once 162 *writeaheadlog.Transaction 163 } 164 165 // newUnappliedWalTxn wraps a `writeaheadlog.Transaction` in an unappliedWalTxn. 166 func newUnappliedWalTxn(t *writeaheadlog.Transaction) *unappliedWalTxn { 167 return &unappliedWalTxn{ 168 Transaction: t, 169 } 170 } 171 172 // SignalUpdatesApplied calls `SignalUpdatesApplied` on the wrapped wal. It will 173 // do so only once. 174 func (t *unappliedWalTxn) SignalUpdatesApplied() error { 175 t.once.Do(func() { 176 t.err = t.Transaction.SignalUpdatesApplied() 177 }) 178 return t.err 179 } 180 181 // newWalTxn creates a new wal transaction and automatically wraps it in an 182 // unappliedWalTxn. 183 func (c *SafeContract) newWalTxn(updates []writeaheadlog.Update) (*unappliedWalTxn, error) { 184 wtxn, err := c.staticWal.NewTransaction(updates) 185 if err != nil { 186 return nil, err 187 } 188 return newUnappliedWalTxn(wtxn), nil 189 } 190 191 // A SafeContract contains the most recent revision transaction negotiated 192 // with a host, and the secret key used to sign it. 193 type SafeContract struct { 194 header contractHeader 195 196 // merkleRoots are the sector roots covered by this contract. 197 merkleRoots *merkleRoots 198 199 // unappliedTxns are the transactions that were written to the WAL but not 200 // applied to the contract file. 201 unappliedTxns []*unappliedWalTxn 202 203 staticDeps modules.Dependencies 204 staticHeaderFile *os.File 205 staticWal *writeaheadlog.WAL 206 mu sync.Mutex 207 208 staticRC *refCounter 209 210 // revisionMu serializes revisions to the contract. It is acquired by 211 // (ContractSet).Acquire and released by (ContractSet).Return. When holding 212 // revisionMu, it is still necessary to lock mu when modifying fields 213 // of the SafeContract. 214 revisionMu sync.Mutex 215 } 216 217 // CommitPaymentIntent will commit the intent to pay a host for an rpc by 218 // committing the signed txn in the contract's header. 219 func (c *SafeContract) CommitPaymentIntent(t *unappliedWalTxn, signedTxn types.Transaction, amount types.Currency, details skymodules.SpendingDetails) error { 220 c.mu.Lock() 221 defer c.mu.Unlock() 222 223 // construct new header 224 newHeader := c.header 225 newHeader.Transaction = signedTxn 226 newHeader.FundAccountSpending = newHeader.FundAccountSpending.Add(details.FundAccountSpending) 227 newHeader.MaintenanceSpending = newHeader.MaintenanceSpending.Add(details.MaintenanceSpending) 228 229 if err := c.applySetHeader(newHeader); err != nil { 230 return err 231 } 232 if err := c.staticHeaderFile.Sync(); err != nil { 233 return err 234 } 235 if err := t.SignalUpdatesApplied(); err != nil { 236 return err 237 } 238 return c.clearUnappliedTxns() 239 } 240 241 // clearUnappliedTxns marks all unapplied transactions as completed without 242 // applying them. 243 func (c *SafeContract) clearUnappliedTxns() error { 244 for len(c.unappliedTxns) > 0 { 245 // Fetch next txn. 246 txn := c.unappliedTxns[0] 247 248 // Mark it as applied. 249 err := txn.SignalUpdatesApplied() 250 if err != nil { 251 return err 252 } 253 254 // Remove it from the contract. In case we crash, we at least won't 255 // start at the beginning again. 256 c.unappliedTxns = c.unappliedTxns[1:] 257 } 258 259 // Set the slice to nil to free memory. 260 c.unappliedTxns = nil 261 return nil 262 } 263 264 // metadata returns the metadata of a renter contract 265 func (c *SafeContract) metadata() skymodules.RenterContract { 266 h := c.header 267 return skymodules.RenterContract{ 268 ID: h.ID(), 269 Transaction: h.copyTransaction(), 270 HostPublicKey: h.HostPublicKey(), 271 StartHeight: h.StartHeight, 272 EndHeight: h.EndHeight(), 273 RenterFunds: h.RenterFunds(), 274 DownloadSpending: h.DownloadSpending, 275 FundAccountSpending: h.FundAccountSpending, 276 MaintenanceSpending: h.MaintenanceSpending, 277 StorageSpending: h.StorageSpending, 278 UploadSpending: h.UploadSpending, 279 TotalCost: h.TotalCost, 280 ContractFee: h.ContractFee, 281 TxnFee: h.TxnFee, 282 SiafundFee: h.SiafundFee, 283 Utility: h.Utility, 284 } 285 } 286 287 // LastRevision returns the most recent revision 288 func (c *SafeContract) LastRevision() types.FileContractRevision { 289 c.mu.Lock() 290 h := c.header 291 c.mu.Unlock() 292 return h.LastRevision() 293 } 294 295 // Metadata returns the metadata of a renter contract 296 func (c *SafeContract) Metadata() skymodules.RenterContract { 297 c.mu.Lock() 298 defer c.mu.Unlock() 299 return c.metadata() 300 } 301 302 // PublicKey returns the public key capable of verifying the renter's signature 303 // on a contract. 304 func (c *SafeContract) PublicKey() crypto.PublicKey { 305 c.mu.Lock() 306 defer c.mu.Unlock() 307 return c.header.SecretKey.PublicKey() 308 } 309 310 // RecordPaymentIntent will records the changes we are about to make to the 311 // revision in order to pay a host for an RPC. 312 func (c *SafeContract) RecordPaymentIntent(rev types.FileContractRevision, amount types.Currency, details skymodules.SpendingDetails) (*unappliedWalTxn, error) { 313 c.mu.Lock() 314 defer c.mu.Unlock() 315 316 newHeader := c.header 317 newHeader.Transaction.FileContractRevisions = []types.FileContractRevision{rev} 318 newHeader.Transaction.TransactionSignatures = nil 319 newHeader.FundAccountSpending = newHeader.FundAccountSpending.Add(details.FundAccountSpending) 320 newHeader.MaintenanceSpending = newHeader.MaintenanceSpending.Add(details.MaintenanceSpending) 321 322 t, err := c.newWalTxn([]writeaheadlog.Update{ 323 c.makeUpdateSetHeader(newHeader), 324 }) 325 if err != nil { 326 return nil, err 327 } 328 if err := <-t.SignalSetupComplete(); err != nil { 329 return nil, err 330 } 331 c.unappliedTxns = append(c.unappliedTxns, t) 332 return t, nil 333 } 334 335 // Sign will sign the given hash using the safecontract's secret key 336 func (c *SafeContract) Sign(hash crypto.Hash) crypto.Signature { 337 c.mu.Lock() 338 defer c.mu.Unlock() 339 return crypto.SignHash(hash, c.header.SecretKey) 340 } 341 342 // UpdateUtility updates the utility field of a contract. 343 func (c *SafeContract) UpdateUtility(utility skymodules.ContractUtility) error { 344 c.mu.Lock() 345 defer c.mu.Unlock() 346 return c.updateUtility(utility) 347 } 348 349 // updateUtility updates the utility field of a contract. 350 func (c *SafeContract) updateUtility(utility skymodules.ContractUtility) error { 351 // If the utility didn't change, this is a no-op. 352 if reflect.DeepEqual(c.header.Utility, utility) { 353 return nil 354 } 355 356 // Construct new header 357 newHeader := c.header 358 newHeader.Utility = utility 359 360 // Record the intent to change the header in the wal. 361 t, err := c.newWalTxn([]writeaheadlog.Update{ 362 c.makeUpdateSetHeader(newHeader), 363 }) 364 if err != nil { 365 return err 366 } 367 // Signal that the setup is completed. 368 if err := <-t.SignalSetupComplete(); err != nil { 369 return err 370 } 371 // Apply the change. 372 if err := c.applySetHeader(newHeader); err != nil { 373 return err 374 } 375 // Sync the change to disk. 376 if err := c.staticHeaderFile.Sync(); err != nil { 377 return err 378 } 379 // Signal that the update has been applied. 380 if err := t.SignalUpdatesApplied(); err != nil { 381 return err 382 } 383 return nil 384 } 385 386 // Utility returns the contract utility for the contract. 387 func (c *SafeContract) Utility() skymodules.ContractUtility { 388 c.mu.Lock() 389 defer c.mu.Unlock() 390 return c.header.Utility 391 } 392 393 // makeUpdateInsertContract creates a writeaheadlog.Update to insert a new 394 // contract into the contractset. 395 func makeUpdateInsertContract(h contractHeader, roots []crypto.Hash) (writeaheadlog.Update, error) { 396 // Validate header. 397 if err := h.validate(); err != nil { 398 return writeaheadlog.Update{}, err 399 } 400 // Create update. 401 return writeaheadlog.Update{ 402 Name: updateNameInsertContract, 403 Instructions: encoding.Marshal(updateInsertContract{ 404 Header: h, 405 Roots: roots, 406 }), 407 }, nil 408 } 409 410 // makeUpdateSetHeader creates an update that changes the header. 411 func (c *SafeContract) makeUpdateSetHeader(h contractHeader) writeaheadlog.Update { 412 id := c.header.ID() 413 return writeaheadlog.Update{ 414 Name: updateNameSetHeader, 415 Instructions: encoding.Marshal(updateSetHeader{ 416 ID: id, 417 Header: h, 418 }), 419 } 420 } 421 422 // makeUpdateSetRoot creates an update that sets a given root, existing or not. 423 func (c *SafeContract) makeUpdateSetRoot(root crypto.Hash, index int) writeaheadlog.Update { 424 id := c.header.ID() 425 return writeaheadlog.Update{ 426 Name: updateNameSetRoot, 427 Instructions: encoding.Marshal(updateSetRoot{ 428 ID: id, 429 Root: root, 430 Index: index, 431 }), 432 } 433 } 434 435 // makeUpdateRefCounterAppend creates a WAL update that sets a given 436 // refcounter value. If there is no open refcounter update session this method 437 // will open one. This update session will be closed when we apply the update. 438 func (c *SafeContract) makeUpdateRefCounterAppend() (writeaheadlog.Update, error) { 439 if build.Release != "testing" { 440 return writeaheadlog.Update{}, nil // no update needed 441 } 442 // TODO This hidden retry is a problem that we need to refactor away, most 443 // probably by refactoring the entire `contract` workflow. The same applies 444 // to `applyRefCounterUpdate`. 445 u, err := c.staticRC.callAppend() 446 // If we don't have an update session open one and try again. 447 if errors.Contains(err, ErrUpdateWithoutUpdateSession) { 448 if err = c.staticRC.callStartUpdate(); err != nil { 449 return writeaheadlog.Update{}, err 450 } 451 u, err = c.staticRC.callAppend() 452 } 453 return u, err 454 } 455 456 // applyRefCounterUpdate applies a refcounter WAL update. If there is no open 457 // update session, it will open one and it will leave it open. This update 458 // session must be closed by the calling method. 459 func (c *SafeContract) applyRefCounterUpdate(u writeaheadlog.Update) error { 460 if build.Release != "testing" { 461 return nil 462 } 463 err := c.staticRC.callCreateAndApplyTransaction(u) 464 // If we don't have an open update session open one and try again. 465 if errors.Contains(err, ErrUpdateWithoutUpdateSession) { 466 if err = c.staticRC.callStartUpdate(); err != nil { 467 return err 468 } 469 err = c.staticRC.callCreateAndApplyTransaction(u) 470 } 471 return err 472 } 473 474 // applySetHeader directly makes changes to the contract header on disk without 475 // going through a WAL transaction. 476 func (c *SafeContract) applySetHeader(h contractHeader) error { 477 if build.DEBUG { 478 // read the existing header on disk, to make sure we aren't overwriting 479 // it with an older revision 480 var oldHeader contractHeader 481 _, err := c.staticHeaderFile.Seek(0, io.SeekStart) 482 if err != nil { 483 build.Critical(err) 484 } 485 headerBytes, err := ioutil.ReadAll(c.staticHeaderFile) 486 if err == nil { 487 if err := encoding.Unmarshal(headerBytes, &oldHeader); err == nil { 488 if oldHeader.LastRevision().NewRevisionNumber > h.LastRevision().NewRevisionNumber { 489 build.Critical("overwriting a newer revision:", oldHeader.LastRevision().NewRevisionNumber, h.LastRevision().NewRevisionNumber) 490 } 491 } 492 } 493 } 494 headerBytes := encoding.Marshal(h) 495 if _, err := c.staticHeaderFile.WriteAt(headerBytes, 0); err != nil { 496 return err 497 } 498 c.header = h 499 return nil 500 } 501 502 // applySetRoot directly sets a given root hash at a given index on disk without 503 // going through a WAL transaction. 504 func (c *SafeContract) applySetRoot(root crypto.Hash, index int) error { 505 return c.merkleRoots.insert(index, root) 506 } 507 508 // managedRecordRootUpdates creates a WAL update that applies a number of 509 // updates to contract roots. 510 func (c *SafeContract) managedRecordRootUpdates(rev types.FileContractRevision, rootUpdates map[uint64]rootUpdate, storageCost, bandwidthCost types.Currency) (*unappliedWalTxn, error) { 511 c.mu.Lock() 512 defer c.mu.Unlock() 513 // construct new header 514 // NOTE: this header will not include the host signature 515 newHeader := c.header 516 newHeader.Transaction.FileContractRevisions = []types.FileContractRevision{rev} 517 newHeader.Transaction.TransactionSignatures = nil 518 newHeader.StorageSpending = newHeader.StorageSpending.Add(storageCost) 519 newHeader.UploadSpending = newHeader.UploadSpending.Add(bandwidthCost) 520 521 updates := []writeaheadlog.Update{ 522 c.makeUpdateSetHeader(newHeader), 523 } 524 for rootIdx, update := range rootUpdates { 525 if !update.trim { 526 updates = append(updates, c.makeUpdateSetRoot(update.root, int(rootIdx))) 527 } else if update.trim && !update.appended { 528 err := errors.New("trimming from a contract is not yet supported") 529 build.Critical(err) 530 return nil, err 531 } 532 } 533 if build.Release == "testing" { 534 rcUpdate, err := c.makeUpdateRefCounterAppend() 535 if err != nil { 536 return nil, errors.AddContext(err, "failed to create a refcounter update") 537 } 538 updates = append(updates, rcUpdate) 539 } 540 t, err := c.newWalTxn(updates) 541 if err != nil { 542 return nil, err 543 } 544 if err := <-t.SignalSetupComplete(); err != nil { 545 return nil, err 546 } 547 c.unappliedTxns = append(c.unappliedTxns, t) 548 return t, nil 549 } 550 551 // managedCommitAppend ignores the header update in the given transaction and 552 // instead applies a new one based on the provided signedTxn. This is necessary 553 // if we run into a desync of contract revisions between renter and host. 554 func (c *SafeContract) managedCommitAppend(t *unappliedWalTxn, signedTxn types.Transaction, storageCost, bandwidthCost types.Currency) error { 555 c.mu.Lock() 556 defer c.mu.Unlock() 557 // construct new header 558 newHeader := c.header 559 newHeader.Transaction = signedTxn 560 newHeader.StorageSpending = newHeader.StorageSpending.Add(storageCost) 561 newHeader.UploadSpending = newHeader.UploadSpending.Add(bandwidthCost) 562 563 // we need this declaration so we don't shadow useful variables further down 564 var err error 565 if err = c.applySetHeader(newHeader); err != nil { 566 return err 567 } 568 569 // pluck the refcounter and setRoot updates from the WAL txn 570 for _, u := range t.Updates { 571 switch u.Name { 572 case updateNameSetHeader: 573 // do nothing - we already applied a new version of this update 574 case updateNameSetRoot: 575 var sru updateSetRoot 576 if err := encoding.Unmarshal(u.Instructions, &sru); err != nil { 577 return err 578 } 579 if err := c.applySetRoot(sru.Root, sru.Index); err != nil { 580 return err 581 } 582 case updateNameRCWriteAt: 583 if err = c.applyRefCounterUpdate(u); err != nil { 584 return errors.AddContext(err, "failed to apply refcounter update") 585 } 586 if err = c.staticRC.callUpdateApplied(); err != nil { 587 return err 588 } 589 default: 590 build.Critical("unexpected update", u.Name) 591 } 592 } 593 594 if err = c.staticHeaderFile.Sync(); err != nil { 595 return err 596 } 597 if err = t.SignalUpdatesApplied(); err != nil { 598 return err 599 } 600 if err := c.clearUnappliedTxns(); err != nil { 601 return errors.AddContext(err, "failed to clear unapplied txns") 602 } 603 return nil 604 } 605 606 // managedRecordDownloadIntent creates a WAL update that updates the header with 607 // the new download costs. 608 func (c *SafeContract) managedRecordDownloadIntent(rev types.FileContractRevision, bandwidthCost types.Currency) (*unappliedWalTxn, error) { 609 c.mu.Lock() 610 defer c.mu.Unlock() 611 // construct new header 612 // NOTE: this header will not include the host signature 613 newHeader := c.header 614 newHeader.Transaction.FileContractRevisions = []types.FileContractRevision{rev} 615 newHeader.Transaction.TransactionSignatures = nil 616 newHeader.DownloadSpending = newHeader.DownloadSpending.Add(bandwidthCost) 617 618 t, err := c.newWalTxn([]writeaheadlog.Update{ 619 c.makeUpdateSetHeader(newHeader), 620 }) 621 if err != nil { 622 return nil, err 623 } 624 if err := <-t.SignalSetupComplete(); err != nil { 625 return nil, err 626 } 627 c.unappliedTxns = append(c.unappliedTxns, t) 628 return t, nil 629 } 630 631 // managedCommitDownload *ignores* all updates in the given transaction and 632 // instead applies the provided signedTxn. See managedCommitAppend. 633 func (c *SafeContract) managedCommitDownload(t *unappliedWalTxn, signedTxn types.Transaction, bandwidthCost types.Currency) error { 634 c.mu.Lock() 635 defer c.mu.Unlock() 636 // construct new header 637 newHeader := c.header 638 newHeader.Transaction = signedTxn 639 newHeader.DownloadSpending = newHeader.DownloadSpending.Add(bandwidthCost) 640 641 if err := c.applySetHeader(newHeader); err != nil { 642 return err 643 } 644 if err := c.staticHeaderFile.Sync(); err != nil { 645 return err 646 } 647 if err := t.SignalUpdatesApplied(); err != nil { 648 return err 649 } 650 if err := c.clearUnappliedTxns(); err != nil { 651 return errors.AddContext(err, "failed to clear unapplied txns") 652 } 653 return nil 654 } 655 656 // managedRecordClearContractIntent records the changes we are about to make to 657 // the revision in the WAL of the contract. 658 func (c *SafeContract) managedRecordClearContractIntent(rev types.FileContractRevision, bandwidthCost types.Currency) (*unappliedWalTxn, error) { 659 c.mu.Lock() 660 defer c.mu.Unlock() 661 // construct new header 662 // NOTE: this header will not include the host signature 663 newHeader := c.header 664 newHeader.Transaction.FileContractRevisions = []types.FileContractRevision{rev} 665 newHeader.Transaction.TransactionSignatures = nil 666 newHeader.UploadSpending = newHeader.UploadSpending.Add(bandwidthCost) 667 668 t, err := c.newWalTxn([]writeaheadlog.Update{ 669 c.makeUpdateSetHeader(newHeader), 670 }) 671 if err != nil { 672 return nil, err 673 } 674 if err := <-t.SignalSetupComplete(); err != nil { 675 return nil, err 676 } 677 c.unappliedTxns = append(c.unappliedTxns, t) 678 return t, nil 679 } 680 681 // managedCommitClearContract commits the changes we made to the revision when 682 // clearing a contract to the WAL of the contract. 683 func (c *SafeContract) managedCommitClearContract(t *unappliedWalTxn, signedTxn types.Transaction, bandwidthCost types.Currency) error { 684 c.mu.Lock() 685 defer c.mu.Unlock() 686 // construct new header 687 newHeader := c.header 688 newHeader.Transaction = signedTxn 689 newHeader.UploadSpending = newHeader.UploadSpending.Add(bandwidthCost) 690 newHeader.Utility.GoodForRefresh = false 691 newHeader.Utility.GoodForRenew = false 692 newHeader.Utility.GoodForUpload = false 693 newHeader.Utility.Locked = true 694 695 if err := c.applySetHeader(newHeader); err != nil { 696 return err 697 } 698 if err := c.staticHeaderFile.Sync(); err != nil { 699 return err 700 } 701 if err := t.SignalUpdatesApplied(); err != nil { 702 return err 703 } 704 if err := c.clearUnappliedTxns(); err != nil { 705 return errors.AddContext(err, "failed to clear unapplied txns") 706 } 707 return nil 708 } 709 710 // managedCommitTxns commits the unapplied transactions to the contract file and marks 711 // the transactions as applied. 712 func (c *SafeContract) managedCommitTxns() error { 713 c.mu.Lock() 714 defer c.mu.Unlock() 715 // We need a way of finding out whether we need to close the refcounter's 716 // update session here. This will only be necessary if there are refcounter 717 // updates in the queue. We don't want to close the session in other cases 718 // because that can close someone else's session. 719 rcUpdatesApplied := false 720 for _, t := range c.unappliedTxns { 721 for _, update := range t.Updates { 722 switch update.Name { 723 case updateNameSetHeader: 724 var u updateSetHeader 725 if err := unmarshalHeader(update.Instructions, &u); err != nil { 726 return err 727 } 728 if err := c.applySetHeader(u.Header); err != nil { 729 return err 730 } 731 case updateNameSetRoot: 732 var u updateSetRoot 733 if err := encoding.Unmarshal(update.Instructions, &u); err != nil { 734 return err 735 } 736 if err := c.applySetRoot(u.Root, u.Index); err != nil { 737 return err 738 } 739 case updateNameRCWriteAt: 740 if err := c.applyRefCounterUpdate(update); err != nil { 741 return err 742 } 743 rcUpdatesApplied = true 744 } 745 } 746 if err := c.staticHeaderFile.Sync(); err != nil { 747 return err 748 } 749 if err := t.SignalUpdatesApplied(); err != nil { 750 return err 751 } 752 } 753 if rcUpdatesApplied { 754 if err := c.staticRC.callUpdateApplied(); err != nil { 755 return err 756 } 757 } 758 c.unappliedTxns = nil 759 return nil 760 } 761 762 // managedSyncRevision checks whether rev accords with the SafeContract's most 763 // recent revision; if it does not, managedSyncRevision attempts to synchronize 764 // with rev by committing any uncommitted WAL transactions. If the revisions 765 // still do not match, and the host's revision is ahead of the renter's, 766 // managedSyncRevision uses the host's revision. Alongside a possible error this 767 // function returns a boolean that indicates whether a resync was attempted. 768 func (c *SafeContract) managedSyncRevision(rev types.FileContractRevision, sigs []types.TransactionSignature) error { 769 c.mu.Lock() 770 defer c.mu.Unlock() 771 772 // Our current revision should always be signed. If it isn't, we have no 773 // choice but to accept the host's revision. 774 if len(c.header.Transaction.TransactionSignatures) == 0 { 775 err := errors.New("renter has unsigned revision, this should never happen") 776 build.Critical(err) 777 return err 778 } 779 780 ourRev := c.header.LastRevision() 781 782 // If the revision number and Merkle root match, we don't need to do 783 // anything. 784 if rev.NewRevisionNumber == ourRev.NewRevisionNumber && rev.NewFileMerkleRoot == ourRev.NewFileMerkleRoot { 785 // If any other fields mismatch, it must be our fault, since we signed 786 // the revision reported by the host. So, to ensure things are 787 // consistent, we blindly overwrite our revision with the host's. 788 c.header.Transaction.FileContractRevisions[0] = rev 789 c.header.Transaction.TransactionSignatures = sigs 790 return nil 791 } 792 793 // The host should never report a lower revision number than ours. If they 794 // do, it may mean they are intentionally (and maliciously) trying to 795 // "rewind" the contract to an earlier state. Even if the host does not have 796 // ill intent, this would mean that they failed to commit one or more 797 // revisions to durable storage, which reflects very poorly on them. 798 if rev.NewRevisionNumber < ourRev.NewRevisionNumber { 799 return &revisionNumberMismatchError{ourRev.NewRevisionNumber, rev.NewRevisionNumber} 800 } 801 802 // At this point, we know that either the host's revision number is above 803 // ours, or their Merkle root differs. Search our unapplied WAL transactions 804 // for one that might synchronize us with the host. 805 for _, t := range c.unappliedTxns { 806 for _, update := range t.Updates { 807 if update.Name == updateNameSetHeader { 808 var u updateSetHeader 809 if err := unmarshalHeader(update.Instructions, &u); err != nil { 810 return err 811 } 812 unappliedRev := u.Header.LastRevision() 813 if unappliedRev.NewRevisionNumber != rev.NewRevisionNumber || unappliedRev.NewFileMerkleRoot != rev.NewFileMerkleRoot { 814 continue 815 } 816 817 // found a matching header, but it still won't have the host's 818 // signatures, since those aren't added until the transaction is 819 // committed. Add the signatures supplied by the host and commit 820 // the header. 821 u.Header.Transaction.TransactionSignatures = sigs 822 if err := c.applySetHeader(u.Header); err != nil { 823 return err 824 } 825 826 // pluck the refcounter and setRoot updates from the WAL txn as 827 // well. 828 for _, u := range t.Updates { 829 switch u.Name { 830 case updateNameSetHeader: 831 // do nothing - we already applied a new version of this update 832 case updateNameSetRoot: 833 var sru updateSetRoot 834 if err := encoding.Unmarshal(u.Instructions, &sru); err != nil { 835 return err 836 } 837 if err := c.applySetRoot(sru.Root, sru.Index); err != nil { 838 return err 839 } 840 case updateNameRCWriteAt: 841 if err := c.applyRefCounterUpdate(u); err != nil { 842 return errors.AddContext(err, "failed to apply refcounter update") 843 } 844 if err := c.staticRC.callUpdateApplied(); err != nil { 845 build.Critical(err) 846 return err 847 } 848 default: 849 build.Critical("unexpected update", u.Name) 850 } 851 } 852 // Sync header. 853 if err := c.staticHeaderFile.Sync(); err != nil { 854 // If syncing to disk fails, panic to avoid further 855 // corruption. 856 panic(err) 857 } 858 // drop all unapplied transactions 859 if err := c.clearUnappliedTxns(); err != nil { 860 return errors.AddContext(err, "failed to clear unapplied txns") 861 } 862 return nil 863 } 864 } 865 } 866 867 // Drop the WAL transactions, since they can't conceivably help us. 868 if err := c.clearUnappliedTxns(); err != nil { 869 return errors.AddContext(err, "failed to clear unapplied txns") 870 } 871 872 // Under certain conditions in testing we want to ignore the fact that we 873 // can't fix the mismatch and simply accept the host's revision. 874 if c.staticDeps.Disrupt("AcceptHostRevision") { 875 c.header.Transaction.FileContractRevisions[0] = rev 876 c.header.Transaction.TransactionSignatures = sigs 877 return nil 878 } 879 880 // The revision mismatch is not fixable since we don't have an open wal txn 881 // that can fix it. Mark the contract as bad for it to be swapped out. This 882 // should never happen. 883 u := c.header.Utility 884 u.GoodForUpload = false 885 u.GoodForRefresh = false 886 u.GoodForRenew = false 887 u.BadContract = true 888 err := fmt.Errorf("revision mismatch unfixable for contract: %v", c.metadata().ID) 889 build.Critical(err) 890 return errors.Compose(c.updateUtility(u), err) 891 } 892 893 // managedInsertContract inserts a contract into the set in an ACID fashion 894 // using the set's WAL. 895 func (cs *ContractSet) managedInsertContract(h contractHeader, roots []crypto.Hash) (skymodules.RenterContract, error) { 896 insertUpdate, err := makeUpdateInsertContract(h, roots) 897 if err != nil { 898 return skymodules.RenterContract{}, err 899 } 900 txn, err := cs.staticWal.NewTransaction([]writeaheadlog.Update{insertUpdate}) 901 if err != nil { 902 return skymodules.RenterContract{}, err 903 } 904 err = <-txn.SignalSetupComplete() 905 if err != nil { 906 return skymodules.RenterContract{}, err 907 } 908 rc, err := cs.managedApplyInsertContractUpdate(insertUpdate) 909 if err != nil { 910 return skymodules.RenterContract{}, err 911 } 912 err = txn.SignalUpdatesApplied() 913 if err != nil { 914 return skymodules.RenterContract{}, err 915 } 916 return rc, nil 917 } 918 919 // managedApplyInsertContractUpdate applies the update to insert a contract into 920 // a set. This will overwrite existing contracts of the same name to make sure 921 // the update is idempotent. 922 func (cs *ContractSet) managedApplyInsertContractUpdate(update writeaheadlog.Update) (skymodules.RenterContract, error) { 923 // Sanity check update. 924 if update.Name != updateNameInsertContract { 925 return skymodules.RenterContract{}, fmt.Errorf("can't call managedApplyInsertContractUpdate on update of type '%v'", update.Name) 926 } 927 // Decode update. 928 var insertUpdate updateInsertContract 929 if err := encoding.UnmarshalAll(update.Instructions, &insertUpdate); err != nil { 930 return skymodules.RenterContract{}, err 931 } 932 h := insertUpdate.Header 933 roots := insertUpdate.Roots 934 // Validate header. 935 if err := h.validate(); err != nil { 936 return skymodules.RenterContract{}, err 937 } 938 headerFilePath := filepath.Join(cs.staticDir, h.ID().String()+contractHeaderExtension) 939 rootsFilePath := filepath.Join(cs.staticDir, h.ID().String()+contractRootsExtension) 940 rcFilePath := filepath.Join(cs.staticDir, h.ID().String()+refCounterExtension) 941 // create the files. 942 headerFile, err := os.OpenFile(headerFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, skymodules.DefaultFilePerm) 943 if err != nil { 944 return skymodules.RenterContract{}, err 945 } 946 rootsFile, err := os.OpenFile(rootsFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, skymodules.DefaultFilePerm) 947 if err != nil { 948 return skymodules.RenterContract{}, err 949 } 950 // write header 951 if _, err := headerFile.Write(encoding.Marshal(h)); err != nil { 952 return skymodules.RenterContract{}, err 953 } 954 // Interrupt if necessary. 955 if cs.staticDeps.Disrupt("InterruptContractInsertion") { 956 return skymodules.RenterContract{}, errors.New("interrupted") 957 } 958 // write roots 959 merkleRoots := newMerkleRoots(rootsFile) 960 for _, root := range roots { 961 if err := merkleRoots.push(root); err != nil { 962 return skymodules.RenterContract{}, err 963 } 964 } 965 // sync both files 966 if err := headerFile.Sync(); err != nil { 967 return skymodules.RenterContract{}, err 968 } 969 if err := rootsFile.Sync(); err != nil { 970 return skymodules.RenterContract{}, err 971 } 972 var rc *refCounter 973 if build.Release == "testing" { 974 rc, err = newRefCounter(rcFilePath, uint64(len(roots)), cs.staticWal) 975 if err != nil { 976 return skymodules.RenterContract{}, errors.AddContext(err, "failed to create a refcounter") 977 } 978 } 979 sc := &SafeContract{ 980 header: h, 981 merkleRoots: merkleRoots, 982 staticDeps: cs.staticDeps, 983 staticHeaderFile: headerFile, 984 staticWal: cs.staticWal, 985 staticRC: rc, 986 } 987 // Compatv144 fix missing void output. 988 cs.mu.Lock() 989 if _, exists := cs.contracts[sc.header.ID()]; exists { 990 build.Critical("trying to overwrite existing contract") 991 } 992 cs.contracts[sc.header.ID()] = sc 993 cs.pubKeys[h.HostPublicKey().String()] = sc.header.ID() 994 cs.mu.Unlock() 995 return sc.Metadata(), nil 996 } 997 998 // loadSafeContractHeader will load a contract from disk, checking for legacy 999 // encodings if initial attempts fail. 1000 func loadSafeContractHeader(f io.ReadSeeker, decodeMaxSize int) (contractHeader, error) { 1001 var header contractHeader 1002 err := encoding.NewDecoder(f, decodeMaxSize).Decode(&header) 1003 if err != nil { 1004 // Try the v160 header. 1005 var v160DecodeErr, v1412DecodeErr error 1006 header, v160DecodeErr = contractHeaderDecodeV1420ToV160(f, decodeMaxSize) 1007 if v160DecodeErr != nil { 1008 // Try the v1412 header. 1009 header, v1412DecodeErr = contractHeaderDecodeV1412ToV160(f, decodeMaxSize) 1010 if v1412DecodeErr != nil { 1011 return contractHeader{}, errors.AddContext(errors.Compose(err, v160DecodeErr, v1412DecodeErr), "unable to decode contract header") 1012 } 1013 } 1014 } 1015 if err := header.validate(); err != nil { 1016 return contractHeader{}, errors.AddContext(err, "unable to validate contract header") 1017 } 1018 return header, nil 1019 } 1020 1021 // loadSafeContract loads a contract from disk and adds it to the contractset 1022 // if it is valid. 1023 func (cs *ContractSet) loadSafeContract(headerFileName, rootsFileName, refCountFileName string, walTxns map[types.FileContractID][]*unappliedWalTxn) (err error) { 1024 headerFile, err := os.OpenFile(headerFileName, os.O_RDWR, skymodules.DefaultFilePerm) 1025 if err != nil { 1026 return err 1027 } 1028 rootsFile, err := os.OpenFile(rootsFileName, os.O_RDWR, skymodules.DefaultFilePerm) 1029 if err != nil { 1030 return err 1031 } 1032 defer func() { 1033 if err != nil { 1034 err = errors.Compose(err, headerFile.Close(), rootsFile.Close()) 1035 } 1036 }() 1037 headerStat, err := headerFile.Stat() 1038 if err != nil { 1039 return err 1040 } 1041 header, err := loadSafeContractHeader(headerFile, int(headerStat.Size())*decodeMaxSizeMultiplier) 1042 if err != nil { 1043 return errors.AddContext(err, "unable to load contract header") 1044 } 1045 1046 // read merkleRoots 1047 merkleRoots, applyTxns, err := loadExistingMerkleRoots(rootsFile) 1048 if err != nil { 1049 return errors.AddContext(err, "unable to load the merkle roots of the contract") 1050 } 1051 var rc *refCounter 1052 if build.Release == "testing" { 1053 // load the reference counter or create a new one if it doesn't exist 1054 rc, err = loadRefCounter(refCountFileName, cs.staticWal) 1055 if errors.Contains(err, ErrRefCounterNotExist) { 1056 rc, err = newRefCounter(refCountFileName, uint64(merkleRoots.numMerkleRoots), cs.staticWal) 1057 } 1058 if err != nil { 1059 return errors.AddContext(err, "failed to load or create a refcounter") 1060 } 1061 } 1062 // add to set 1063 sc := &SafeContract{ 1064 header: header, 1065 merkleRoots: merkleRoots, 1066 unappliedTxns: walTxns[header.ID()], 1067 staticDeps: cs.staticDeps, 1068 staticHeaderFile: headerFile, 1069 staticWal: cs.staticWal, 1070 staticRC: rc, 1071 } 1072 1073 // apply the wal txns if necessary. 1074 if applyTxns { 1075 if err := sc.managedCommitTxns(); err != nil { 1076 return errors.AddContext(err, "unable to commit the wal transactions during contractset recovery") 1077 } 1078 } 1079 if _, exists := cs.contracts[sc.header.ID()]; exists { 1080 build.Critical("trying to overwrite existing contract") 1081 } 1082 cs.contracts[sc.header.ID()] = sc 1083 cs.pubKeys[header.HostPublicKey().String()] = sc.header.ID() 1084 return nil 1085 } 1086 1087 // ConvertV130Contract creates a contract file for a v130 contract. 1088 func (cs *ContractSet) ConvertV130Contract(c V130Contract, cr V130CachedRevision) error { 1089 m, err := cs.managedInsertContract(contractHeader{ 1090 Transaction: c.LastRevisionTxn, 1091 SecretKey: c.SecretKey, 1092 StartHeight: c.StartHeight, 1093 DownloadSpending: c.DownloadSpending, 1094 StorageSpending: c.StorageSpending, 1095 UploadSpending: c.UploadSpending, 1096 TotalCost: c.TotalCost, 1097 ContractFee: c.ContractFee, 1098 TxnFee: c.TxnFee, 1099 SiafundFee: c.SiafundFee, 1100 }, c.MerkleRoots) 1101 if err != nil { 1102 return err 1103 } 1104 // if there is a cached revision, store it as an unapplied WAL transaction 1105 if cr.Revision.NewRevisionNumber != 0 { 1106 sc, ok := cs.Acquire(m.ID) 1107 if !ok { 1108 return errors.New("contract set is missing contract that was just added") 1109 } 1110 defer cs.Return(sc) 1111 if len(cr.MerkleRoots) == sc.merkleRoots.len()+1 { 1112 root := cr.MerkleRoots[len(cr.MerkleRoots)-1] 1113 _, err = sc.managedRecordRootUpdates(cr.Revision, map[uint64]rootUpdate{ 1114 uint64(len(cr.MerkleRoots)): newRootUpdateAppendRoot(root), 1115 }, types.ZeroCurrency, types.ZeroCurrency) 1116 } else { 1117 _, err = sc.managedRecordDownloadIntent(cr.Revision, types.ZeroCurrency) 1118 } 1119 if err != nil { 1120 return err 1121 } 1122 } 1123 return nil 1124 } 1125 1126 // A V130Contract specifies the v130 contract format. 1127 type V130Contract struct { 1128 HostPublicKey types.SiaPublicKey `json:"hostpublickey"` 1129 ID types.FileContractID `json:"id"` 1130 LastRevision types.FileContractRevision `json:"lastrevision"` 1131 LastRevisionTxn types.Transaction `json:"lastrevisiontxn"` 1132 MerkleRoots MerkleRootSet `json:"merkleroots"` 1133 SecretKey crypto.SecretKey `json:"secretkey"` 1134 StartHeight types.BlockHeight `json:"startheight"` 1135 DownloadSpending types.Currency `json:"downloadspending"` 1136 StorageSpending types.Currency `json:"storagespending"` 1137 UploadSpending types.Currency `json:"uploadspending"` 1138 TotalCost types.Currency `json:"totalcost"` 1139 ContractFee types.Currency `json:"contractfee"` 1140 TxnFee types.Currency `json:"txnfee"` 1141 SiafundFee types.Currency `json:"siafundfee"` 1142 } 1143 1144 // EndHeight returns the height at which the host is no longer obligated to 1145 // store contract data. 1146 func (c *V130Contract) EndHeight() types.BlockHeight { 1147 return c.LastRevision.NewWindowStart 1148 } 1149 1150 // RenterFunds returns the funds remaining in the contract's Renter payout as 1151 // of the most recent revision. 1152 func (c *V130Contract) RenterFunds() types.Currency { 1153 if len(c.LastRevision.NewValidProofOutputs) < 2 { 1154 return types.ZeroCurrency 1155 } 1156 return c.LastRevision.ValidRenterPayout() 1157 } 1158 1159 // A V130CachedRevision contains changes that would be applied to a 1160 // RenterContract if a contract revision succeeded. 1161 type V130CachedRevision struct { 1162 Revision types.FileContractRevision `json:"revision"` 1163 MerkleRoots skymodules.MerkleRootSet `json:"merkleroots"` 1164 } 1165 1166 // MerkleRootSet is a set of Merkle roots, and gets encoded more efficiently. 1167 type MerkleRootSet []crypto.Hash 1168 1169 // MarshalJSON defines a JSON encoding for a MerkleRootSet. 1170 func (mrs MerkleRootSet) MarshalJSON() ([]byte, error) { 1171 // Copy the whole array into a giant byte slice and then encode that. 1172 fullBytes := make([]byte, crypto.HashSize*len(mrs)) 1173 for i := range mrs { 1174 copy(fullBytes[i*crypto.HashSize:(i+1)*crypto.HashSize], mrs[i][:]) 1175 } 1176 return json.Marshal(fullBytes) 1177 } 1178 1179 // UnmarshalJSON attempts to decode a MerkleRootSet, falling back on the legacy 1180 // decoding of a []crypto.Hash if that fails. 1181 func (mrs *MerkleRootSet) UnmarshalJSON(b []byte) error { 1182 // Decode the giant byte slice, and then split it into separate arrays. 1183 var fullBytes []byte 1184 err := json.Unmarshal(b, &fullBytes) 1185 if err != nil { 1186 // Encoding the byte slice has failed, try decoding it as a []crypto.Hash. 1187 var hashes []crypto.Hash 1188 err := json.Unmarshal(b, &hashes) 1189 if err != nil { 1190 return err 1191 } 1192 *mrs = MerkleRootSet(hashes) 1193 return nil 1194 } 1195 1196 umrs := make(MerkleRootSet, len(fullBytes)/32) 1197 for i := range umrs { 1198 copy(umrs[i][:], fullBytes[i*crypto.HashSize:(i+1)*crypto.HashSize]) 1199 } 1200 *mrs = umrs 1201 return nil 1202 } 1203 1204 // unmarshalHeader loads the header of a file contract. The load processes 1205 // starts by attempting to load the contract assuming it is the most recent 1206 // version of the contract. If that fails, it'll try increasingly older versions 1207 // of the contract until it either succeeds or it runs out of decoding 1208 // strategies to try. 1209 func unmarshalHeader(b []byte, u *updateSetHeader) error { 1210 // Try unmarshalling the header. 1211 if err := encoding.Unmarshal(b, u); err != nil { 1212 // Try unmarshalling the update 1213 v1420Err := updateSetHeaderUnmarshalV1420ToV160(b, u) 1214 if v1420Err != nil { 1215 v132Err := updateSetHeaderUnmarshalV132ToV160(b, u) 1216 if v132Err != nil { 1217 return errors.AddContext(errors.Compose(err, v1420Err, v132Err), "unable to unmarshal update set header") 1218 } 1219 } 1220 } 1221 return nil 1222 }