Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 18 additions & 50 deletions funding/commitment_type_negotiation.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,61 +18,30 @@ var (
// negotiateCommitmentType negotiates the commitment type of a newly opened
// channel. If a desiredChanType 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.
// Otherwise, a default type is selected based on feature compatibility,
// particularly when the RPC caller does not request a specific channel type.
//
// The returned ChannelType is nil when implicit negotiation is used. An error
// is only returned if desiredChanType is not supported.
// The returned ChannelType is always non-nil. An error is only returned if
// desiredChanType is not supported.
func negotiateCommitmentType(desiredChanType *lnwire.ChannelType, local,
remote *lnwire.FeatureVector) (*lnwire.ChannelType,
lnwallet.CommitmentType, error) {

// BOLT#2 specifies we MUST use explicit negotiation if both peers
// signal for it.
explicitNegotiation := hasFeatures(
local, remote, lnwire.ExplicitChannelTypeOptional,
)

chanTypeRequested := desiredChanType != nil

switch {
case explicitNegotiation && chanTypeRequested:
// If a specific channel type was provided, verify it's supported.
if desiredChanType != nil {
commitType, err := explicitNegotiateCommitmentType(
*desiredChanType, local, remote,
)

return desiredChanType, commitType, err
}

// We don't have a specific channel type requested, so we select a
// default type as if implicit negotiation were used, and then we
// explicitly signal that default type.
case explicitNegotiation && !chanTypeRequested:
defaultChanType, commitType := implicitNegotiateCommitmentType(
local, remote,
)

return defaultChanType, commitType, nil

// A specific channel type was requested, but we can't explicitly signal
// it. So if implicit negotiation wouldn't select the desired channel
// type, we must return an error.
case !explicitNegotiation && chanTypeRequested:
implicitChanType, commitType := implicitNegotiateCommitmentType(
local, remote,
)

expected := lnwire.RawFeatureVector(*desiredChanType)
actual := lnwire.RawFeatureVector(*implicitChanType)
if !expected.Equals(&actual) {
return nil, 0, errUnsupportedChannelType
}

return nil, commitType, nil

default: // !explicitNegotiation && !chanTypeRequested
_, commitType := implicitNegotiateCommitmentType(local, remote)
// No specific channel type was requested. Select a default type based
// on locally-known feature compatibility. This default is then sent
// explicitly over the wire.
defaultChanType, commitType := selectDefaultChannelType(local, remote)

return nil, commitType, nil
}
return defaultChanType, commitType, nil
}

// explicitNegotiateCommitmentType attempts to explicitly negotiate for a
Expand Down Expand Up @@ -454,15 +423,14 @@ func explicitNegotiateCommitmentType(channelType lnwire.ChannelType, local,
}
}

// implicitNegotiateCommitmentType negotiates the commitment type of a channel
// implicitly by choosing the latest non-taproot type supported by the local and
// remote features. Taproot channels must be requested explicitly, keeping
// implicit opens on channel types that can be used for both public and private
// channels.
// selectDefaultChannelType selects a default channel type by choosing the
// latest non-taproot type supported by the local and remote features.
// Taproot channels must be requested explicitly, keeping default selections
// on channel types that can be used for both public and private channels.
//
// TODO(yy): Revisit implicit taproot negotiation once public taproot channel
// TODO(yy): Revisit taproot channel selection once public taproot channel
// announcements are supported.
func implicitNegotiateCommitmentType(local,
func selectDefaultChannelType(local,
remote *lnwire.FeatureVector) (*lnwire.ChannelType,
lnwallet.CommitmentType) {

Expand Down
36 changes: 23 additions & 13 deletions funding/commitment_type_negotiation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,14 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
lnwire.StaticRemoteKeyOptional,
lnwire.AnchorsZeroFeeHtlcTxOptional,
),
//nolint:ll
expectsCommitType: lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx,
expectsChanType: nil,
expectsErr: nil,
expectsChanType: (*lnwire.ChannelType)(
lnwire.NewRawFeatureVector(
lnwire.StaticRemoteKeyRequired,
lnwire.AnchorsZeroFeeHtlcTxRequired,
),
),
expectsErr: nil,
},
{
name: "explicit missing remote commitment feature",
Expand Down Expand Up @@ -282,7 +286,7 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
expectsErr: nil,
},
{
name: "implicit tweakless",
name: "default tweakless",
channelFeatures: nil,
localFeatures: lnwire.NewRawFeatureVector(
lnwire.StaticRemoteKeyRequired,
Expand All @@ -292,20 +296,26 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
lnwire.StaticRemoteKeyOptional,
),
expectsCommitType: lnwallet.CommitmentTypeTweakless,
expectsChanType: nil,
expectsErr: nil,
expectsChanType: (*lnwire.ChannelType)(
lnwire.NewRawFeatureVector(
lnwire.StaticRemoteKeyRequired,
),
),
expectsErr: nil,
},
{
name: "implicit legacy",
name: "default legacy",
channelFeatures: nil,
localFeatures: lnwire.NewRawFeatureVector(),
remoteFeatures: lnwire.NewRawFeatureVector(
lnwire.StaticRemoteKeyOptional,
lnwire.AnchorsZeroFeeHtlcTxOptional,
),
expectsCommitType: lnwallet.CommitmentTypeLegacy,
expectsChanType: nil,
expectsErr: nil,
expectsChanType: (*lnwire.ChannelType)(
lnwire.NewRawFeatureVector(),
),
expectsErr: nil,
},

// Test cases for final taproot channels with explicit
Expand Down Expand Up @@ -432,11 +442,11 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
expectsErr: errUnsupportedChannelType,
},

// Test cases for implicit negotiation ignoring taproot feature
// Test cases for default negotiation ignoring taproot feature
// bits. Taproot channels require an explicit channel type.
{
//nolint:ll
name: "implicit anchors preferred over taproot",
name: "default anchors preferred over taproot",
channelFeatures: nil,
localFeatures: lnwire.NewRawFeatureVector(
lnwire.AnchorsZeroFeeHtlcTxOptional,
Expand All @@ -461,7 +471,7 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
},
{
//nolint:ll
name: "implicit ignores staging taproot without anchors",
name: "default ignores staging taproot without anchors",
channelFeatures: nil,
localFeatures: lnwire.NewRawFeatureVector(
lnwire.SimpleTaprootChannelsOptionalFinal,
Expand All @@ -480,7 +490,7 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
},
{
//nolint:ll
name: "implicit ignores final taproot without anchors",
name: "default ignores final taproot without anchors",
channelFeatures: nil,
localFeatures: lnwire.NewRawFeatureVector(
lnwire.SimpleTaprootChannelsOptionalFinal,
Expand Down
Loading
Loading