github.com/Synthesix/Sia@v1.3.3-0.20180413141344-f863baeed3ca/modules/renter/proto/contract.go (about)

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