Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lnwire+funding: introduce new protocol extension for explicit commitment type negotiation #5669

Merged
merged 11 commits into from
Sep 1, 2021
Merged
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
22 changes: 22 additions & 0 deletions cmd/lncli/cmd_open_channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ Signed base64 encoded PSBT or hex encoded raw wire TX (or path to text file): `
// the user from choosing a large file by accident and running into out
// of memory issues or other weird errors.
psbtMaxFileSize = 1024 * 1024

channelTypeTweakless = "tweakless"
channelTypeAnchors = "anchors"
)

// TODO(roasbeef): change default number of confirmations
Expand Down Expand Up @@ -200,6 +203,12 @@ var openChannelCommand = cli.Command{
Usage: "(optional) the maximum value in msat that " +
"can be pending within the channel at any given time",
},
cli.StringFlag{
Name: "channel_type",
Usage: fmt.Sprintf("(optional) the type of channel to "+
"propose to the remote peer (%q, %q)",
channelTypeTweakless, channelTypeAnchors),
},
},
Action: actionDecorator(openChannel),
}
Expand Down Expand Up @@ -307,6 +316,19 @@ func openChannel(ctx *cli.Context) error {

req.Private = ctx.Bool("private")

// Parse the channel type and map it to its RPC representation.
channelType := ctx.String("channel_type")
switch channelType {
case "":
break
case channelTypeTweakless:
req.CommitmentType = lnrpc.CommitmentType_STATIC_REMOTE_KEY
case channelTypeAnchors:
req.CommitmentType = lnrpc.CommitmentType_ANCHORS
default:
return fmt.Errorf("unsupported channel type %v", channelType)
}

// PSBT funding is a more involved, interactive process that is too
// large to also fit into this already long function.
if ctx.Bool("psbt") {
Expand Down
12 changes: 12 additions & 0 deletions docs/release-notes/release-notes-0.14.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ instantaneous. Read the [guide on leader
election](https://github.com/lightningnetwork/lnd/blob/master/docs/leader_election.md)
for more information.

## Protocol Extensions

### Explicit Channel Negotiation

[A new protocol extension has been added known as explicit channel negotiation]
(https://github.com/lightningnetwork/lnd/pull/5669). This allows a channel
initiator to signal their desired channel type to use with the remote peer. If
the remote peer supports said channel type and agrees, the previous implicit
negotiation based on the shared set of feature bits is bypassed, and the
proposed channel type is used.

## RPC Server

* [Return payment address and add index from
Expand Down Expand Up @@ -238,6 +249,7 @@ change](https://github.com/lightningnetwork/lnd/pull/5613).
* Eugene Siegel
* Martin Habovstiak
* Oliver Gugger
* Wilmer Paulino
* xanoni
* Yong Yu
* Zero-1729
4 changes: 4 additions & 0 deletions feature/default_sets.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,8 @@ var defaultSetDesc = setDesc{
lnwire.AMPRequired: {
SetInvoiceAmp: {}, // 9A
},
lnwire.ExplicitChannelTypeOptional: {
SetInit: {}, // I
SetNodeAnn: {}, // N
},
}
4 changes: 4 additions & 0 deletions feature/deps.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,13 @@ var deps = depDesc{
lnwire.AnchorsOptional: {
lnwire.StaticRemoteKeyOptional: {},
},
lnwire.AnchorsZeroFeeHtlcTxOptional: {
lnwire.StaticRemoteKeyOptional: {},
},
lnwire.AMPOptional: {
lnwire.PaymentAddrOptional: {},
},
lnwire.ExplicitChannelTypeOptional: {},
}

// ValidateDeps asserts that a feature vector sets all features and their
Expand Down
120 changes: 120 additions & 0 deletions funding/commitment_type_negotiation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package funding

import (
"errors"

"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/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) (lnwallet.CommitmentType, error) {

if channelType != nil {
if !hasFeatures(local, remote, lnwire.ExplicitChannelTypeOptional) {
return 0, errUnsupportedExplicitNegotiation
}
return explicitNegotiateCommitmentType(
*channelType, local, remote,
)
}

return implicitNegotiateCommitmentType(local, remote), 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 {
// 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) 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) {
return 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) {
return lnwallet.CommitmentTypeTweakless
}

// Otherwise we'll fall back to the legacy type.
return 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
}
Loading