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 }