github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/host/negotiaterecentrevision.go (about)

     1  package host
     2  
     3  import (
     4  	"crypto/rand"
     5  	"errors"
     6  	"net"
     7  	"time"
     8  
     9  	"github.com/NebulousLabs/Sia/crypto"
    10  	"github.com/NebulousLabs/Sia/encoding"
    11  	"github.com/NebulousLabs/Sia/modules"
    12  	"github.com/NebulousLabs/Sia/types"
    13  
    14  	"github.com/NebulousLabs/bolt"
    15  )
    16  
    17  var (
    18  	// errRevisionFewPublicKeys is returned when a stored file contract
    19  	// revision does not have enough public keys - such a situation should
    20  	// never happen, and is a critical / developer error.
    21  	errRevisionFewPublicKeys = errors.New("too few public keys in the unlock conditions of the file contract revision")
    22  )
    23  
    24  // verifyChallengeResponse will verify that the renter's response to the
    25  // challenge provided by the host is correct. In the process, the storage
    26  // obligation and file contract revision will be loaded and returned.
    27  func (h *Host) verifyChallengeResponse(fcid types.FileContractID, challenge crypto.Hash, challengeResponse crypto.Signature) (*storageObligation, types.FileContractRevision, []types.TransactionSignature, error) {
    28  	// Fetch the storage obligation, which has the revision, which has the
    29  	// renter's public key.
    30  	var so *storageObligation
    31  	err := h.db.Update(func(tx *bolt.Tx) error {
    32  		fso, err := getStorageObligation(tx, fcid)
    33  		so = &fso
    34  		return err
    35  	})
    36  	if err != nil {
    37  		return nil, types.FileContractRevision{}, nil, err
    38  	}
    39  
    40  	// Pull out the file contract revision and the revision's signatures from
    41  	// the transaction.
    42  	revisionTxn := so.RevisionTransactionSet[len(so.RevisionTransactionSet)-1]
    43  	recentRevision := revisionTxn.FileContractRevisions[0]
    44  	var revisionSigs []types.TransactionSignature
    45  	for _, sig := range revisionTxn.TransactionSignatures {
    46  		// Checking for just the parent id is sufficient, an over-signed file
    47  		// contract is invalid.
    48  		if sig.ParentID == crypto.Hash(fcid) {
    49  			revisionSigs = append(revisionSigs, sig)
    50  		}
    51  	}
    52  
    53  	// Verify that the challegne response matches the public key.
    54  	var renterPK crypto.PublicKey
    55  	// Sanity check - there should be two public keys.
    56  	if len(recentRevision.UnlockConditions.PublicKeys) != 2 {
    57  		h.log.Critical("found a revision with too few public keys")
    58  		return nil, types.FileContractRevision{}, nil, errRevisionFewPublicKeys
    59  	}
    60  	copy(renterPK[:], recentRevision.UnlockConditions.PublicKeys[0].Key)
    61  	err = crypto.VerifyHash(challenge, renterPK, challengeResponse)
    62  	if err != nil {
    63  		return nil, types.FileContractRevision{}, nil, err
    64  	}
    65  	return so, recentRevision, revisionSigs, nil
    66  }
    67  
    68  // managedRPCRecentRevision sends the most recent known file contract
    69  // revision, including signatures, to the renter, for the file contract with
    70  // the input id.
    71  func (h *Host) managedRPCRecentRevision(conn net.Conn) (types.FileContractID, *storageObligation, error) {
    72  	// Set the negotiation deadline.
    73  	conn.SetDeadline(time.Now().Add(modules.NegotiateRecentRevisionTime))
    74  
    75  	// Receive the file contract id from the renter.
    76  	var fcid types.FileContractID
    77  	err := encoding.ReadObject(conn, &fcid, uint64(len(fcid)))
    78  	if err != nil {
    79  		return types.FileContractID{}, nil, err
    80  	}
    81  
    82  	// Send a challenge to the renter to verify that the renter has write
    83  	// access to the revision being opened.
    84  	var challenge crypto.Hash
    85  	_, err = rand.Read(challenge[:])
    86  	if err != nil {
    87  		return types.FileContractID{}, nil, err
    88  	}
    89  	err = encoding.WriteObject(conn, challenge)
    90  	if err != nil {
    91  		return types.FileContractID{}, nil, err
    92  	}
    93  
    94  	// Read the signed response from the renter.
    95  	var challengeResponse crypto.Signature
    96  	err = encoding.ReadObject(conn, &challengeResponse, uint64(len(challengeResponse)))
    97  	if err != nil {
    98  		return types.FileContractID{}, nil, err
    99  	}
   100  	// Verify the response. In the process, fetch the related storage
   101  	// obligation, file contract revision, and transaction signatures.
   102  	so, recentRevision, revisionSigs, err := h.verifyChallengeResponse(fcid, challenge, challengeResponse)
   103  	if err != nil {
   104  		return types.FileContractID{}, nil, modules.WriteNegotiationRejection(conn, err)
   105  	}
   106  
   107  	// Send the file contract revision and the corresponding signatures to the
   108  	// renter.
   109  	err = modules.WriteNegotiationAcceptance(conn)
   110  	if err != nil {
   111  		return types.FileContractID{}, nil, err
   112  	}
   113  	err = encoding.WriteObject(conn, recentRevision)
   114  	if err != nil {
   115  		return types.FileContractID{}, nil, err
   116  	}
   117  	err = encoding.WriteObject(conn, revisionSigs)
   118  	if err != nil {
   119  		return types.FileContractID{}, nil, err
   120  	}
   121  	return fcid, so, nil
   122  }