github.com/decred/dcrlnd@v0.7.6/funding/commitment_type_negotiation.go (about)

     1  package funding
     2  
     3  import (
     4  	"errors"
     5  
     6  	"github.com/decred/dcrlnd/lnwallet"
     7  	"github.com/decred/dcrlnd/lnwire"
     8  )
     9  
    10  var (
    11  	// errUnsupportedExplicitNegotiation is an error returned when explicit
    12  	// channel commitment negotiation is attempted but either peer of the
    13  	// channel does not support it.
    14  	errUnsupportedExplicitNegotiation = errors.New("explicit channel " +
    15  		"type negotiation not supported")
    16  
    17  	// errUnsupportedCommitmentType is an error returned when a specific
    18  	// channel commitment type is being explicitly negotiated but either
    19  	// peer of the channel does not support it.
    20  	errUnsupportedChannelType = errors.New("requested channel type " +
    21  		"not supported")
    22  )
    23  
    24  // negotiateCommitmentType negotiates the commitment type of a newly opened
    25  // channel. If a channelType is provided, explicit negotiation for said type
    26  // will be attempted if the set of both local and remote features support it.
    27  // Otherwise, implicit negotiation will be attempted.
    28  func negotiateCommitmentType(channelType *lnwire.ChannelType,
    29  	local, remote *lnwire.FeatureVector, mustBeExplicit bool,
    30  ) (bool, *lnwire.ChannelType, lnwallet.CommitmentType, error) {
    31  
    32  	if channelType != nil {
    33  		// If the peer does know explicit negotiation, let's attempt
    34  		// that now.
    35  		if hasFeatures(local, remote, lnwire.ExplicitChannelTypeOptional) {
    36  			chanType, err := explicitNegotiateCommitmentType(
    37  				*channelType, local, remote,
    38  			)
    39  			return true, channelType, chanType, err
    40  		}
    41  
    42  		// If we're the funder, and we are attempting to use an
    43  		// explicit channel type, but the remote party doesn't signal
    44  		// the bit, then we actually want to exit here, to ensure the
    45  		// user doesn't end up with an unexpected channel type via
    46  		// implicit negotiation.
    47  		if mustBeExplicit {
    48  			return false, nil, 0, errUnsupportedExplicitNegotiation
    49  		}
    50  	}
    51  
    52  	chanType, commitType := implicitNegotiateCommitmentType(local, remote)
    53  	return false, chanType, commitType, nil
    54  }
    55  
    56  // explicitNegotiateCommitmentType attempts to explicitly negotiate for a
    57  // specific channel type. Since the channel type is comprised of a set of even
    58  // feature bits, we also make sure each feature is supported by both peers. An
    59  // error is returned if either peer does not support said channel type.
    60  func explicitNegotiateCommitmentType(channelType lnwire.ChannelType,
    61  	local, remote *lnwire.FeatureVector) (lnwallet.CommitmentType, error) {
    62  
    63  	channelFeatures := lnwire.RawFeatureVector(channelType)
    64  
    65  	switch {
    66  	// Lease script enforcement + anchors zero fee + static remote key
    67  	// features only.
    68  	case channelFeatures.OnlyContains(
    69  		lnwire.ScriptEnforcedLeaseRequired,
    70  		lnwire.AnchorsZeroFeeHtlcTxRequired,
    71  		lnwire.StaticRemoteKeyRequired,
    72  	):
    73  		if !hasFeatures(
    74  			local, remote,
    75  			lnwire.ScriptEnforcedLeaseOptional,
    76  			lnwire.AnchorsZeroFeeHtlcTxOptional,
    77  			lnwire.StaticRemoteKeyOptional,
    78  		) {
    79  			return 0, errUnsupportedChannelType
    80  		}
    81  		return lnwallet.CommitmentTypeScriptEnforcedLease, nil
    82  
    83  	// Anchors zero fee + static remote key features only.
    84  	case channelFeatures.OnlyContains(
    85  		lnwire.AnchorsZeroFeeHtlcTxRequired,
    86  		lnwire.StaticRemoteKeyRequired,
    87  	):
    88  		if !hasFeatures(
    89  			local, remote,
    90  			lnwire.AnchorsZeroFeeHtlcTxOptional,
    91  			lnwire.StaticRemoteKeyOptional,
    92  		) {
    93  			return 0, errUnsupportedChannelType
    94  		}
    95  		return lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx, nil
    96  
    97  	// Static remote key feature only.
    98  	case channelFeatures.OnlyContains(lnwire.StaticRemoteKeyRequired):
    99  		if !hasFeatures(local, remote, lnwire.StaticRemoteKeyOptional) {
   100  			return 0, errUnsupportedChannelType
   101  		}
   102  		return lnwallet.CommitmentTypeTweakless, nil
   103  
   104  	// No features, use legacy commitment type.
   105  	case channelFeatures.IsEmpty():
   106  		return lnwallet.CommitmentTypeLegacy, nil
   107  
   108  	default:
   109  		return 0, errUnsupportedChannelType
   110  	}
   111  }
   112  
   113  // implicitNegotiateCommitmentType negotiates the commitment type of a channel
   114  // implicitly by choosing the latest type supported by the local and remote
   115  // fetures.
   116  func implicitNegotiateCommitmentType(local,
   117  	remote *lnwire.FeatureVector) (*lnwire.ChannelType, lnwallet.CommitmentType) {
   118  
   119  	// If both peers are signalling support for anchor commitments with
   120  	// zero-fee HTLC transactions, we'll use this type.
   121  	if hasFeatures(local, remote, lnwire.AnchorsZeroFeeHtlcTxOptional) {
   122  		chanType := lnwire.ChannelType(*lnwire.NewRawFeatureVector(
   123  			lnwire.AnchorsZeroFeeHtlcTxRequired,
   124  			lnwire.StaticRemoteKeyRequired,
   125  		))
   126  
   127  		return &chanType, lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx
   128  	}
   129  
   130  	// Since we don't want to support the "legacy" anchor type, we will fall
   131  	// back to static remote key if the nodes don't support the zero fee
   132  	// HTLC tx anchor type.
   133  	//
   134  	// If both nodes are signaling the proper feature bit for tweakless
   135  	// commitments, we'll use that.
   136  	if hasFeatures(local, remote, lnwire.StaticRemoteKeyOptional) {
   137  		chanType := lnwire.ChannelType(*lnwire.NewRawFeatureVector(
   138  			lnwire.StaticRemoteKeyRequired,
   139  		))
   140  
   141  		return &chanType, lnwallet.CommitmentTypeTweakless
   142  	}
   143  
   144  	// Otherwise we'll fall back to the legacy type.
   145  	chanType := lnwire.ChannelType(*lnwire.NewRawFeatureVector())
   146  	return &chanType, lnwallet.CommitmentTypeLegacy
   147  }
   148  
   149  // hasFeatures determines whether a set of features is supported by both the set
   150  // of local and remote features.
   151  func hasFeatures(local, remote *lnwire.FeatureVector,
   152  	features ...lnwire.FeatureBit) bool {
   153  
   154  	for _, feature := range features {
   155  		if !local.HasFeature(feature) || !remote.HasFeature(feature) {
   156  			return false
   157  		}
   158  	}
   159  	return true
   160  }