gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/renter/proto/contract.go (about)

     1  package proto
     2  
     3  import (
     4  	"encoding/json"
     5  	"os"
     6  	"path/filepath"
     7  	"sync"
     8  
     9  	"gitlab.com/NebulousLabs/errors"
    10  
    11  	"gitlab.com/SiaPrime/SiaPrime/build"
    12  	"gitlab.com/SiaPrime/SiaPrime/crypto"
    13  	"gitlab.com/SiaPrime/SiaPrime/encoding"
    14  	"gitlab.com/SiaPrime/SiaPrime/modules"
    15  	"gitlab.com/SiaPrime/SiaPrime/types"
    16  	"gitlab.com/SiaPrime/writeaheadlog"
    17  )
    18  
    19  const (
    20  	// contractHeaderSize is the maximum amount of space that the non-Merkle-root
    21  	// portion of a contract can consume.
    22  	contractHeaderSize = writeaheadlog.MaxPayloadSize // TODO: test this
    23  
    24  	updateNameSetHeader = "setHeader"
    25  	updateNameSetRoot   = "setRoot"
    26  )
    27  
    28  type updateSetHeader struct {
    29  	ID     types.FileContractID
    30  	Header contractHeader
    31  }
    32  
    33  // v132UpdateHeader was introduced due to backwards compatibility reasons after
    34  // changing the format of the contractHeader. It contains the legacy
    35  // v132ContractHeader.
    36  type v132UpdateSetHeader struct {
    37  	ID     types.FileContractID
    38  	Header v132ContractHeader
    39  }
    40  
    41  type updateSetRoot struct {
    42  	ID    types.FileContractID
    43  	Root  crypto.Hash
    44  	Index int
    45  }
    46  
    47  type contractHeader struct {
    48  	// transaction is the signed transaction containing the most recent
    49  	// revision of the file contract.
    50  	Transaction types.Transaction
    51  
    52  	// secretKey is the key used by the renter to sign the file contract
    53  	// transaction.
    54  	SecretKey crypto.SecretKey
    55  
    56  	// Same as modules.RenterContract.
    57  	StartHeight      types.BlockHeight
    58  	DownloadSpending types.Currency
    59  	StorageSpending  types.Currency
    60  	UploadSpending   types.Currency
    61  	TotalCost        types.Currency
    62  	ContractFee      types.Currency
    63  	TxnFee           types.Currency
    64  	SiafundFee       types.Currency
    65  	Utility          modules.ContractUtility
    66  }
    67  
    68  // v132ContractHeader is a contractHeader without the Utility field. This field
    69  // was added after v132 to be able to persist contract utilities.
    70  type v132ContractHeader struct {
    71  	// transaction is the signed transaction containing the most recent
    72  	// revision of the file contract.
    73  	Transaction types.Transaction
    74  
    75  	// secretKey is the key used by the renter to sign the file contract
    76  	// transaction.
    77  	SecretKey crypto.SecretKey
    78  
    79  	// Same as modules.RenterContract.
    80  	StartHeight      types.BlockHeight
    81  	DownloadSpending types.Currency
    82  	StorageSpending  types.Currency
    83  	UploadSpending   types.Currency
    84  	TotalCost        types.Currency
    85  	ContractFee      types.Currency
    86  	TxnFee           types.Currency
    87  	SiafundFee       types.Currency
    88  }
    89  
    90  // validate returns an error if the contractHeader is invalid.
    91  func (h *contractHeader) validate() error {
    92  	if len(h.Transaction.FileContractRevisions) > 0 &&
    93  		len(h.Transaction.FileContractRevisions[0].NewValidProofOutputs) > 0 &&
    94  		len(h.Transaction.FileContractRevisions[0].UnlockConditions.PublicKeys) == 2 {
    95  		return nil
    96  	}
    97  	return errors.New("invalid contract")
    98  }
    99  
   100  func (h *contractHeader) copyTransaction() (txn types.Transaction) {
   101  	encoding.Unmarshal(encoding.Marshal(h.Transaction), &txn)
   102  	return
   103  }
   104  
   105  func (h *contractHeader) LastRevision() types.FileContractRevision {
   106  	return h.Transaction.FileContractRevisions[0]
   107  }
   108  
   109  func (h *contractHeader) ID() types.FileContractID {
   110  	return h.LastRevision().ID()
   111  }
   112  
   113  func (h *contractHeader) HostPublicKey() types.SiaPublicKey {
   114  	return h.LastRevision().HostPublicKey()
   115  }
   116  
   117  func (h *contractHeader) RenterFunds() types.Currency {
   118  	return h.LastRevision().RenterFunds()
   119  }
   120  
   121  func (h *contractHeader) EndHeight() types.BlockHeight {
   122  	return h.LastRevision().EndHeight()
   123  }
   124  
   125  // A SafeContract contains the most recent revision transaction negotiated
   126  // with a host, and the secret key used to sign it.
   127  type SafeContract struct {
   128  	header contractHeader
   129  
   130  	// merkleRoots are the sector roots covered by this contract.
   131  	merkleRoots *merkleRoots
   132  
   133  	// unappliedTxns are the transactions that were written to the WAL but not
   134  	// applied to the contract file.
   135  	unappliedTxns []*writeaheadlog.Transaction
   136  
   137  	headerFile *fileSection
   138  	wal        *writeaheadlog.WAL
   139  	mu         sync.Mutex
   140  
   141  	// revisionMu serializes revisions to the contract. It is acquired by
   142  	// (ContractSet).Acquire and released by (ContractSet).Return. When holding
   143  	// revisionMu, it is still necessary to lock mu when modifying fields of the
   144  	// SafeContract.
   145  	revisionMu sync.Mutex
   146  }
   147  
   148  // Metadata returns the metadata of a renter contract
   149  func (c *SafeContract) Metadata() modules.RenterContract {
   150  	c.mu.Lock()
   151  	defer c.mu.Unlock()
   152  	h := c.header
   153  	return modules.RenterContract{
   154  		ID:               h.ID(),
   155  		Transaction:      h.copyTransaction(),
   156  		HostPublicKey:    h.HostPublicKey(),
   157  		StartHeight:      h.StartHeight,
   158  		EndHeight:        h.EndHeight(),
   159  		RenterFunds:      h.RenterFunds(),
   160  		DownloadSpending: h.DownloadSpending,
   161  		StorageSpending:  h.StorageSpending,
   162  		UploadSpending:   h.UploadSpending,
   163  		TotalCost:        h.TotalCost,
   164  		ContractFee:      h.ContractFee,
   165  		TxnFee:           h.TxnFee,
   166  		SiafundFee:       h.SiafundFee,
   167  		Utility:          h.Utility,
   168  	}
   169  }
   170  
   171  // UpdateUtility updates the utility field of a contract.
   172  func (c *SafeContract) UpdateUtility(utility modules.ContractUtility) error {
   173  	c.mu.Lock()
   174  	defer c.mu.Unlock()
   175  	// Construct new header
   176  	newHeader := c.header
   177  	newHeader.Utility = utility
   178  
   179  	// Record the intent to change the header in the wal.
   180  	t, err := c.wal.NewTransaction([]writeaheadlog.Update{
   181  		c.makeUpdateSetHeader(newHeader),
   182  	})
   183  	if err != nil {
   184  		return err
   185  	}
   186  	// Signal that the setup is completed.
   187  	if err := <-t.SignalSetupComplete(); err != nil {
   188  		return err
   189  	}
   190  	// Apply the change.
   191  	if err := c.applySetHeader(newHeader); err != nil {
   192  		return err
   193  	}
   194  	// Sync the change to disk.
   195  	if err := c.headerFile.Sync(); err != nil {
   196  		return err
   197  	}
   198  	// Signal that the update has been applied.
   199  	if err := t.SignalUpdatesApplied(); err != nil {
   200  		return err
   201  	}
   202  	return nil
   203  }
   204  
   205  // Utility returns the contract utility for the contract.
   206  func (c *SafeContract) Utility() modules.ContractUtility {
   207  	c.mu.Lock()
   208  	defer c.mu.Unlock()
   209  	return c.header.Utility
   210  }
   211  
   212  func (c *SafeContract) makeUpdateSetHeader(h contractHeader) writeaheadlog.Update {
   213  	id := c.header.ID()
   214  	return writeaheadlog.Update{
   215  		Name: updateNameSetHeader,
   216  		Instructions: encoding.Marshal(updateSetHeader{
   217  			ID:     id,
   218  			Header: h,
   219  		}),
   220  	}
   221  }
   222  
   223  func (c *SafeContract) makeUpdateSetRoot(root crypto.Hash, index int) writeaheadlog.Update {
   224  	id := c.header.ID()
   225  	return writeaheadlog.Update{
   226  		Name: updateNameSetRoot,
   227  		Instructions: encoding.Marshal(updateSetRoot{
   228  			ID:    id,
   229  			Root:  root,
   230  			Index: index,
   231  		}),
   232  	}
   233  }
   234  
   235  func (c *SafeContract) applySetHeader(h contractHeader) error {
   236  	if build.DEBUG {
   237  		// read the existing header on disk, to make sure we aren't overwriting
   238  		// it with an older revision
   239  		var oldHeader contractHeader
   240  		headerBytes := make([]byte, contractHeaderSize)
   241  		if _, err := c.headerFile.ReadAt(headerBytes, 0); err == nil {
   242  			if err := encoding.Unmarshal(headerBytes, &oldHeader); err == nil {
   243  				if oldHeader.LastRevision().NewRevisionNumber > h.LastRevision().NewRevisionNumber {
   244  					build.Critical("overwriting a newer revision:", oldHeader.LastRevision().NewRevisionNumber, h.LastRevision().NewRevisionNumber)
   245  				}
   246  			}
   247  		}
   248  	}
   249  
   250  	headerBytes := make([]byte, contractHeaderSize)
   251  	copy(headerBytes, encoding.Marshal(h))
   252  	if _, err := c.headerFile.WriteAt(headerBytes, 0); err != nil {
   253  		return err
   254  	}
   255  	c.header = h
   256  	return nil
   257  }
   258  
   259  func (c *SafeContract) applySetRoot(root crypto.Hash, index int) error {
   260  	return c.merkleRoots.insert(index, root)
   261  }
   262  
   263  func (c *SafeContract) managedRecordUploadIntent(rev types.FileContractRevision, root crypto.Hash, storageCost, bandwidthCost types.Currency) (*writeaheadlog.Transaction, error) {
   264  	c.mu.Lock()
   265  	defer c.mu.Unlock()
   266  	// construct new header
   267  	// NOTE: this header will not include the host signature
   268  	newHeader := c.header
   269  	newHeader.Transaction.FileContractRevisions = []types.FileContractRevision{rev}
   270  	newHeader.Transaction.TransactionSignatures = nil
   271  	newHeader.StorageSpending = newHeader.StorageSpending.Add(storageCost)
   272  	newHeader.UploadSpending = newHeader.UploadSpending.Add(bandwidthCost)
   273  
   274  	t, err := c.wal.NewTransaction([]writeaheadlog.Update{
   275  		c.makeUpdateSetHeader(newHeader),
   276  		c.makeUpdateSetRoot(root, c.merkleRoots.len()),
   277  	})
   278  	if err != nil {
   279  		return nil, err
   280  	}
   281  	if err := <-t.SignalSetupComplete(); err != nil {
   282  		return nil, err
   283  	}
   284  	c.unappliedTxns = append(c.unappliedTxns, t)
   285  	return t, nil
   286  }
   287  
   288  func (c *SafeContract) managedCommitUpload(t *writeaheadlog.Transaction, signedTxn types.Transaction, root crypto.Hash, storageCost, bandwidthCost types.Currency) error {
   289  	c.mu.Lock()
   290  	defer c.mu.Unlock()
   291  	// construct new header
   292  	newHeader := c.header
   293  	newHeader.Transaction = signedTxn
   294  	newHeader.StorageSpending = newHeader.StorageSpending.Add(storageCost)
   295  	newHeader.UploadSpending = newHeader.UploadSpending.Add(bandwidthCost)
   296  
   297  	if err := c.applySetHeader(newHeader); err != nil {
   298  		return err
   299  	}
   300  	if err := c.applySetRoot(root, c.merkleRoots.len()); err != nil {
   301  		return err
   302  	}
   303  	if err := c.headerFile.Sync(); err != nil {
   304  		return err
   305  	}
   306  	if err := t.SignalUpdatesApplied(); err != nil {
   307  		return err
   308  	}
   309  	c.unappliedTxns = nil
   310  	return nil
   311  }
   312  
   313  func (c *SafeContract) managedRecordDownloadIntent(rev types.FileContractRevision, bandwidthCost types.Currency) (*writeaheadlog.Transaction, error) {
   314  	c.mu.Lock()
   315  	defer c.mu.Unlock()
   316  	// construct new header
   317  	// NOTE: this header will not include the host signature
   318  	newHeader := c.header
   319  	newHeader.Transaction.FileContractRevisions = []types.FileContractRevision{rev}
   320  	newHeader.Transaction.TransactionSignatures = nil
   321  	newHeader.DownloadSpending = newHeader.DownloadSpending.Add(bandwidthCost)
   322  
   323  	t, err := c.wal.NewTransaction([]writeaheadlog.Update{
   324  		c.makeUpdateSetHeader(newHeader),
   325  	})
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  	if err := <-t.SignalSetupComplete(); err != nil {
   330  		return nil, err
   331  	}
   332  	c.unappliedTxns = append(c.unappliedTxns, t)
   333  	return t, nil
   334  }
   335  
   336  func (c *SafeContract) managedCommitDownload(t *writeaheadlog.Transaction, signedTxn types.Transaction, bandwidthCost types.Currency) error {
   337  	c.mu.Lock()
   338  	defer c.mu.Unlock()
   339  	// construct new header
   340  	newHeader := c.header
   341  	newHeader.Transaction = signedTxn
   342  	newHeader.DownloadSpending = newHeader.DownloadSpending.Add(bandwidthCost)
   343  
   344  	if err := c.applySetHeader(newHeader); err != nil {
   345  		return err
   346  	}
   347  	if err := c.headerFile.Sync(); err != nil {
   348  		return err
   349  	}
   350  	if err := t.SignalUpdatesApplied(); err != nil {
   351  		return err
   352  	}
   353  	c.unappliedTxns = nil
   354  	return nil
   355  }
   356  
   357  // managedCommitTxns commits the unapplied transactions to the contract file and marks
   358  // the transactions as applied.
   359  func (c *SafeContract) managedCommitTxns() error {
   360  	c.mu.Lock()
   361  	defer c.mu.Unlock()
   362  	for _, t := range c.unappliedTxns {
   363  		for _, update := range t.Updates {
   364  			switch update.Name {
   365  			case updateNameSetHeader:
   366  				var u updateSetHeader
   367  				if err := unmarshalHeader(update.Instructions, &u); err != nil {
   368  					return err
   369  				}
   370  				if err := c.applySetHeader(u.Header); err != nil {
   371  					return err
   372  				}
   373  			case updateNameSetRoot:
   374  				var u updateSetRoot
   375  				if err := encoding.Unmarshal(update.Instructions, &u); err != nil {
   376  					return err
   377  				}
   378  				if err := c.applySetRoot(u.Root, u.Index); err != nil {
   379  					return err
   380  				}
   381  			}
   382  		}
   383  		if err := c.headerFile.Sync(); err != nil {
   384  			return err
   385  		}
   386  		if err := t.SignalUpdatesApplied(); err != nil {
   387  			return err
   388  		}
   389  	}
   390  	c.unappliedTxns = nil
   391  	return nil
   392  }
   393  
   394  // managedSyncRevision checks whether rev accords with the SafeContract's most
   395  // recent revision; if it does not, managedSyncRevision attempts to synchronize
   396  // with rev by committing any uncommitted WAL transactions. If the revisions
   397  // still do not match, and the host's revision is ahead of the renter's,
   398  // managedSyncRevision uses the host's revision.
   399  func (c *SafeContract) managedSyncRevision(rev types.FileContractRevision, sigs []types.TransactionSignature) error {
   400  	c.mu.Lock()
   401  	defer c.mu.Unlock()
   402  
   403  	// Our current revision should always be signed. If it isn't, we have no
   404  	// choice but to accept the host's revision.
   405  	if len(c.header.Transaction.TransactionSignatures) == 0 {
   406  		c.header.Transaction.FileContractRevisions[0] = rev
   407  		c.header.Transaction.TransactionSignatures = sigs
   408  		return nil
   409  	}
   410  
   411  	ourRev := c.header.LastRevision()
   412  
   413  	// If the revision number and Merkle root match, we don't need to do anything.
   414  	if rev.NewRevisionNumber == ourRev.NewRevisionNumber && rev.NewFileMerkleRoot == ourRev.NewFileMerkleRoot {
   415  		// If any other fields mismatch, it must be our fault, since we signed
   416  		// the revision reported by the host. So, to ensure things are
   417  		// consistent, we blindly overwrite our revision with the host's.
   418  		c.header.Transaction.FileContractRevisions[0] = rev
   419  		c.header.Transaction.TransactionSignatures = sigs
   420  		return nil
   421  	}
   422  
   423  	// The host should never report a lower revision number than ours. If they
   424  	// do, it may mean they are intentionally (and maliciously) trying to
   425  	// "rewind" the contract to an earlier state. Even if the host does not have
   426  	// ill intent, this would mean that they failed to commit one or more
   427  	// revisions to durable storage, which reflects very poorly on them.
   428  	if rev.NewRevisionNumber < ourRev.NewRevisionNumber {
   429  		return &revisionNumberMismatchError{ourRev.NewRevisionNumber, rev.NewRevisionNumber}
   430  	}
   431  
   432  	// At this point, we know that either the host's revision number is above
   433  	// ours, or their Merkle root differs. Search our unapplied WAL transactions
   434  	// for one that might synchronize us with the host.
   435  	for _, t := range c.unappliedTxns {
   436  		for _, update := range t.Updates {
   437  			if update.Name == updateNameSetHeader {
   438  				var u updateSetHeader
   439  				if err := unmarshalHeader(update.Instructions, &u); err != nil {
   440  					return err
   441  				}
   442  				unappliedRev := u.Header.LastRevision()
   443  				if unappliedRev.NewRevisionNumber != rev.NewRevisionNumber || unappliedRev.NewFileMerkleRoot != rev.NewFileMerkleRoot {
   444  					continue
   445  				}
   446  				// found a matching header, but it still won't have the host's
   447  				// signatures, since those aren't added until the transaction is
   448  				// committed. Add the signatures supplied by the host and commit
   449  				// the header.
   450  				u.Header.Transaction.TransactionSignatures = sigs
   451  				if err := c.applySetHeader(u.Header); err != nil {
   452  					return err
   453  				}
   454  				if err := c.headerFile.Sync(); err != nil {
   455  					return err
   456  				}
   457  				// drop all unapplied transactions
   458  				for _, t := range c.unappliedTxns {
   459  					if err := t.SignalUpdatesApplied(); err != nil {
   460  						return err
   461  					}
   462  				}
   463  				c.unappliedTxns = nil
   464  				return nil
   465  			}
   466  		}
   467  	}
   468  
   469  	// The host's revision is still different, and we have no unapplied
   470  	// transactions containing their revision. At this point, the best we can do
   471  	// is accept their revision. This isn't ideal, but at least there's no
   472  	// security risk, since we *did* sign the revision that the host is
   473  	// claiming. Worst case, certain contract metadata (e.g. UploadSpending)
   474  	// will be incorrect.
   475  	c.header.Transaction.FileContractRevisions[0] = rev
   476  	c.header.Transaction.TransactionSignatures = sigs
   477  	// Drop the WAL transactions, since they can't conceivably help us.
   478  	for _, t := range c.unappliedTxns {
   479  		if err := t.SignalUpdatesApplied(); err != nil {
   480  			return err
   481  		}
   482  	}
   483  	c.unappliedTxns = nil
   484  	return nil
   485  }
   486  
   487  func (cs *ContractSet) managedInsertContract(h contractHeader, roots []crypto.Hash) (modules.RenterContract, error) {
   488  	if err := h.validate(); err != nil {
   489  		return modules.RenterContract{}, err
   490  	}
   491  	f, err := os.Create(filepath.Join(cs.dir, h.ID().String()+contractExtension))
   492  	if err != nil {
   493  		return modules.RenterContract{}, err
   494  	}
   495  	// create fileSections
   496  	headerSection := newFileSection(f, 0, contractHeaderSize)
   497  	rootsSection := newFileSection(f, contractHeaderSize, -1)
   498  	// write header
   499  	if _, err := headerSection.WriteAt(encoding.Marshal(h), 0); err != nil {
   500  		return modules.RenterContract{}, err
   501  	}
   502  	// write roots
   503  	merkleRoots := newMerkleRoots(rootsSection)
   504  	for _, root := range roots {
   505  		if err := merkleRoots.push(root); err != nil {
   506  			return modules.RenterContract{}, err
   507  		}
   508  	}
   509  	if err := f.Sync(); err != nil {
   510  		return modules.RenterContract{}, err
   511  	}
   512  	sc := &SafeContract{
   513  		header:      h,
   514  		merkleRoots: merkleRoots,
   515  		headerFile:  headerSection,
   516  		wal:         cs.wal,
   517  	}
   518  	cs.mu.Lock()
   519  	cs.contracts[sc.header.ID()] = sc
   520  	cs.pubKeys[h.HostPublicKey().String()] = sc.header.ID()
   521  	cs.mu.Unlock()
   522  	return sc.Metadata(), nil
   523  }
   524  
   525  // loadSafeContract loads a contract from disk and adds it to the contractset
   526  // if it is valid.
   527  func (cs *ContractSet) loadSafeContract(filename string, walTxns []*writeaheadlog.Transaction) (err error) {
   528  	f, err := os.OpenFile(filename, os.O_RDWR, 0600)
   529  	if err != nil {
   530  		return err
   531  	}
   532  	defer func() {
   533  		if err != nil {
   534  			f.Close()
   535  		}
   536  	}()
   537  	stat, err := f.Stat()
   538  	if err != nil {
   539  		return err
   540  	}
   541  
   542  	headerSection := newFileSection(f, 0, contractHeaderSize)
   543  	rootsSection := newFileSection(f, contractHeaderSize, remainingFile)
   544  
   545  	// read header
   546  	var header contractHeader
   547  	if err := encoding.NewDecoder(f, int(stat.Size()*3)).Decode(&header); err != nil {
   548  		return err
   549  	} else if err := header.validate(); err != nil {
   550  		return err
   551  	}
   552  
   553  	// read merkleRoots
   554  	merkleRoots, applyTxns, err := loadExistingMerkleRoots(rootsSection)
   555  	if err != nil {
   556  		return err
   557  	}
   558  	// add relevant unapplied transactions
   559  	var unappliedTxns []*writeaheadlog.Transaction
   560  	for _, t := range walTxns {
   561  		// NOTE: we assume here that if any of the updates apply to the
   562  		// contract, the whole transaction applies to the contract.
   563  		if len(t.Updates) == 0 {
   564  			continue
   565  		}
   566  		var id types.FileContractID
   567  		switch update := t.Updates[0]; update.Name {
   568  		case updateNameSetHeader:
   569  			var u updateSetHeader
   570  			if err := unmarshalHeader(update.Instructions, &u); err != nil {
   571  				return err
   572  			}
   573  			id = u.ID
   574  		case updateNameSetRoot:
   575  			var u updateSetRoot
   576  			if err := encoding.Unmarshal(update.Instructions, &u); err != nil {
   577  				return err
   578  			}
   579  			id = u.ID
   580  		}
   581  		if id == header.ID() {
   582  			unappliedTxns = append(unappliedTxns, t)
   583  		}
   584  	}
   585  	// add to set
   586  	sc := &SafeContract{
   587  		header:        header,
   588  		merkleRoots:   merkleRoots,
   589  		unappliedTxns: unappliedTxns,
   590  		headerFile:    headerSection,
   591  		wal:           cs.wal,
   592  	}
   593  
   594  	// apply the wal txns if necessary.
   595  	if applyTxns {
   596  		if err := sc.managedCommitTxns(); err != nil {
   597  			return err
   598  		}
   599  	}
   600  	cs.contracts[sc.header.ID()] = sc
   601  	cs.pubKeys[header.HostPublicKey().String()] = sc.header.ID()
   602  	return nil
   603  }
   604  
   605  // ConvertV130Contract creates a contract file for a v130 contract.
   606  func (cs *ContractSet) ConvertV130Contract(c V130Contract, cr V130CachedRevision) error {
   607  	m, err := cs.managedInsertContract(contractHeader{
   608  		Transaction:      c.LastRevisionTxn,
   609  		SecretKey:        c.SecretKey,
   610  		StartHeight:      c.StartHeight,
   611  		DownloadSpending: c.DownloadSpending,
   612  		StorageSpending:  c.StorageSpending,
   613  		UploadSpending:   c.UploadSpending,
   614  		TotalCost:        c.TotalCost,
   615  		ContractFee:      c.ContractFee,
   616  		TxnFee:           c.TxnFee,
   617  		SiafundFee:       c.SiafundFee,
   618  	}, c.MerkleRoots)
   619  	if err != nil {
   620  		return err
   621  	}
   622  	// if there is a cached revision, store it as an unapplied WAL transaction
   623  	if cr.Revision.NewRevisionNumber != 0 {
   624  		sc, ok := cs.Acquire(m.ID)
   625  		if !ok {
   626  			return errors.New("contract set is missing contract that was just added")
   627  		}
   628  		defer cs.Return(sc)
   629  		if len(cr.MerkleRoots) == sc.merkleRoots.len()+1 {
   630  			root := cr.MerkleRoots[len(cr.MerkleRoots)-1]
   631  			_, err = sc.managedRecordUploadIntent(cr.Revision, root, types.ZeroCurrency, types.ZeroCurrency)
   632  		} else {
   633  			_, err = sc.managedRecordDownloadIntent(cr.Revision, types.ZeroCurrency)
   634  		}
   635  		if err != nil {
   636  			return err
   637  		}
   638  	}
   639  	return nil
   640  }
   641  
   642  // A V130Contract specifies the v130 contract format.
   643  type V130Contract struct {
   644  	HostPublicKey    types.SiaPublicKey         `json:"hostpublickey"`
   645  	ID               types.FileContractID       `json:"id"`
   646  	LastRevision     types.FileContractRevision `json:"lastrevision"`
   647  	LastRevisionTxn  types.Transaction          `json:"lastrevisiontxn"`
   648  	MerkleRoots      MerkleRootSet              `json:"merkleroots"`
   649  	SecretKey        crypto.SecretKey           `json:"secretkey"`
   650  	StartHeight      types.BlockHeight          `json:"startheight"`
   651  	DownloadSpending types.Currency             `json:"downloadspending"`
   652  	StorageSpending  types.Currency             `json:"storagespending"`
   653  	UploadSpending   types.Currency             `json:"uploadspending"`
   654  	TotalCost        types.Currency             `json:"totalcost"`
   655  	ContractFee      types.Currency             `json:"contractfee"`
   656  	TxnFee           types.Currency             `json:"txnfee"`
   657  	SiafundFee       types.Currency             `json:"siafundfee"`
   658  }
   659  
   660  // EndHeight returns the height at which the host is no longer obligated to
   661  // store contract data.
   662  func (c *V130Contract) EndHeight() types.BlockHeight {
   663  	return c.LastRevision.NewWindowStart
   664  }
   665  
   666  // RenterFunds returns the funds remaining in the contract's Renter payout as
   667  // of the most recent revision.
   668  func (c *V130Contract) RenterFunds() types.Currency {
   669  	if len(c.LastRevision.NewValidProofOutputs) < 2 {
   670  		return types.ZeroCurrency
   671  	}
   672  	return c.LastRevision.NewValidProofOutputs[0].Value
   673  }
   674  
   675  // A V130CachedRevision contains changes that would be applied to a
   676  // RenterContract if a contract revision succeeded.
   677  type V130CachedRevision struct {
   678  	Revision    types.FileContractRevision `json:"revision"`
   679  	MerkleRoots modules.MerkleRootSet      `json:"merkleroots"`
   680  }
   681  
   682  // MerkleRootSet is a set of Merkle roots, and gets encoded more efficiently.
   683  type MerkleRootSet []crypto.Hash
   684  
   685  // MarshalJSON defines a JSON encoding for a MerkleRootSet.
   686  func (mrs MerkleRootSet) MarshalJSON() ([]byte, error) {
   687  	// Copy the whole array into a giant byte slice and then encode that.
   688  	fullBytes := make([]byte, crypto.HashSize*len(mrs))
   689  	for i := range mrs {
   690  		copy(fullBytes[i*crypto.HashSize:(i+1)*crypto.HashSize], mrs[i][:])
   691  	}
   692  	return json.Marshal(fullBytes)
   693  }
   694  
   695  // UnmarshalJSON attempts to decode a MerkleRootSet, falling back on the legacy
   696  // decoding of a []crypto.Hash if that fails.
   697  func (mrs *MerkleRootSet) UnmarshalJSON(b []byte) error {
   698  	// Decode the giant byte slice, and then split it into separate arrays.
   699  	var fullBytes []byte
   700  	err := json.Unmarshal(b, &fullBytes)
   701  	if err != nil {
   702  		// Encoding the byte slice has failed, try decoding it as a []crypto.Hash.
   703  		var hashes []crypto.Hash
   704  		err := json.Unmarshal(b, &hashes)
   705  		if err != nil {
   706  			return err
   707  		}
   708  		*mrs = MerkleRootSet(hashes)
   709  		return nil
   710  	}
   711  
   712  	umrs := make(MerkleRootSet, len(fullBytes)/32)
   713  	for i := range umrs {
   714  		copy(umrs[i][:], fullBytes[i*crypto.HashSize:(i+1)*crypto.HashSize])
   715  	}
   716  	*mrs = umrs
   717  	return nil
   718  }
   719  
   720  func unmarshalHeader(b []byte, u *updateSetHeader) error {
   721  	// Try unmarshaling the header.
   722  	if err := encoding.Unmarshal(b, u); err != nil {
   723  		// COMPATv132 try unmarshaling the header the old way.
   724  		var oldHeader v132UpdateSetHeader
   725  		if err2 := encoding.Unmarshal(b, &oldHeader); err2 != nil {
   726  			// If unmarshaling the header the old way also doesn't work we
   727  			// return the original error.
   728  			return err
   729  		}
   730  		// If unmarshaling it the old way was successful we convert it to a new
   731  		// header.
   732  		u.Header = contractHeader{
   733  			Transaction:      oldHeader.Header.Transaction,
   734  			SecretKey:        oldHeader.Header.SecretKey,
   735  			StartHeight:      oldHeader.Header.StartHeight,
   736  			DownloadSpending: oldHeader.Header.DownloadSpending,
   737  			StorageSpending:  oldHeader.Header.StorageSpending,
   738  			UploadSpending:   oldHeader.Header.UploadSpending,
   739  			TotalCost:        oldHeader.Header.TotalCost,
   740  			ContractFee:      oldHeader.Header.ContractFee,
   741  			TxnFee:           oldHeader.Header.TxnFee,
   742  			SiafundFee:       oldHeader.Header.SiafundFee,
   743  		}
   744  	}
   745  	return nil
   746  }