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