github.com/decred/dcrlnd@v0.7.6/watchtower/wtserver/create_session.go (about) 1 package wtserver 2 3 import ( 4 "github.com/decred/dcrlnd/input" 5 "github.com/decred/dcrlnd/watchtower/blob" 6 "github.com/decred/dcrlnd/watchtower/wtdb" 7 "github.com/decred/dcrlnd/watchtower/wtpolicy" 8 "github.com/decred/dcrlnd/watchtower/wtwire" 9 ) 10 11 // handleCreateSession processes a CreateSession message from the peer, and returns 12 // a CreateSessionReply in response. This method will only succeed if no existing 13 // session info is known about the session id. If an existing session is found, 14 // the reward address is returned in case the client lost our reply. 15 func (s *Server) handleCreateSession(peer Peer, id *wtdb.SessionID, 16 req *wtwire.CreateSession) error { 17 18 // TODO(conner): validate accept against policy 19 20 // Query the db for session info belonging to the client's session id. 21 existingInfo, err := s.cfg.DB.GetSessionInfo(id) 22 switch { 23 24 // We already have a session, though it is currently unused. We'll allow 25 // the client to recommit the session if it wanted to change the policy. 26 case err == nil && existingInfo.LastApplied == 0: 27 28 // We already have a session corresponding to this session id, return an 29 // error signaling that it already exists in our database. We return the 30 // reward address to the client in case they were not able to process 31 // our reply earlier. 32 case err == nil && existingInfo.LastApplied > 0: 33 log.Debugf("Already have session for %s", id) 34 return s.replyCreateSession( 35 peer, id, wtwire.CreateSessionCodeAlreadyExists, 36 existingInfo.LastApplied, existingInfo.RewardAddress, 37 ) 38 39 // Some other database error occurred, return a temporary failure. 40 case err != wtdb.ErrSessionNotFound: 41 log.Errorf("unable to load session info for %s", id) 42 return s.replyCreateSession( 43 peer, id, wtwire.CodeTemporaryFailure, 0, nil, 44 ) 45 } 46 47 // Ensure that the requested blob type is supported by our tower. 48 if !blob.IsSupportedType(req.BlobType) { 49 log.Debugf("Rejecting CreateSession from %s, unsupported blob "+ 50 "type %s", id, req.BlobType) 51 return s.replyCreateSession( 52 peer, id, wtwire.CreateSessionCodeRejectBlobType, 0, 53 nil, 54 ) 55 } 56 57 // If the request asks for a reward session and the tower has them 58 // disabled, we will reject the request. 59 if s.cfg.DisableReward && req.BlobType.Has(blob.FlagReward) { 60 log.Debugf("Rejecting CreateSession from %s, reward "+ 61 "sessions disabled", id) 62 return s.replyCreateSession( 63 peer, id, wtwire.CreateSessionCodeRejectBlobType, 0, 64 nil, 65 ) 66 } 67 68 // Now that we've established that this session does not exist in the 69 // database, retrieve the sweep address that will be given to the 70 // client. This address is to be included by the client when signing 71 // sweep transactions destined for this tower, if its negotiated output 72 // is not dust. 73 var rewardScript []byte 74 if req.BlobType.Has(blob.FlagReward) { 75 rewardAddress, err := s.cfg.NewAddress() 76 if err != nil { 77 log.Errorf("Unable to generate reward addr for %s: %v", 78 id, err) 79 return s.replyCreateSession( 80 peer, id, wtwire.CodeTemporaryFailure, 0, nil, 81 ) 82 } 83 84 // Construct the pkscript the client should pay to when signing 85 // justice transactions for this session. 86 rewardScript, err = input.PayToAddrScript(rewardAddress) 87 if err != nil { 88 log.Errorf("Unable to generate reward script for "+ 89 "%s: %v", id, err) 90 return s.replyCreateSession( 91 peer, id, wtwire.CodeTemporaryFailure, 0, nil, 92 ) 93 } 94 } 95 96 // TODO(conner): create invoice for upfront payment 97 98 // Assemble the session info using the agreed upon parameters, reward 99 // address, and session id. 100 info := wtdb.SessionInfo{ 101 ID: *id, 102 Policy: wtpolicy.Policy{ 103 TxPolicy: wtpolicy.TxPolicy{ 104 BlobType: req.BlobType, 105 RewardBase: req.RewardBase, 106 RewardRate: req.RewardRate, 107 SweepFeeRate: req.SweepFeeRate, 108 }, 109 MaxUpdates: req.MaxUpdates, 110 }, 111 RewardAddress: rewardScript, 112 } 113 114 // Insert the session info into the watchtower's database. If 115 // successful, the session will now be ready for use. 116 err = s.cfg.DB.InsertSessionInfo(&info) 117 if err != nil { 118 log.Errorf("Unable to create session for %s: %v", id, err) 119 return s.replyCreateSession( 120 peer, id, wtwire.CodeTemporaryFailure, 0, nil, 121 ) 122 } 123 124 log.Infof("Accepted session for %s", id) 125 126 return s.replyCreateSession( 127 peer, id, wtwire.CodeOK, 0, rewardScript, 128 ) 129 } 130 131 // replyCreateSession sends a response to a CreateSession from a client. If the 132 // status code in the reply is OK, the error from the write will be bubbled up. 133 // Otherwise, this method returns a connection error to ensure we don't continue 134 // communication with the client. 135 func (s *Server) replyCreateSession(peer Peer, id *wtdb.SessionID, 136 code wtwire.ErrorCode, lastApplied uint16, data []byte) error { 137 138 if s.cfg.NoAckCreateSession { 139 return &connFailure{ 140 ID: *id, 141 Code: code, 142 } 143 } 144 145 msg := &wtwire.CreateSessionReply{ 146 Code: code, 147 LastApplied: lastApplied, 148 Data: data, 149 } 150 151 err := s.sendMessage(peer, msg) 152 if err != nil { 153 log.Errorf("unable to send CreateSessionReply to %s", id) 154 } 155 156 // Return the write error if the request succeeded. 157 if code == wtwire.CodeOK { 158 return err 159 } 160 161 // Otherwise the request failed, return a connection failure to 162 // disconnect the client. 163 return &connFailure{ 164 ID: *id, 165 Code: code, 166 } 167 }