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  }