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 }