Skip to content
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
9 changes: 9 additions & 0 deletions chanbackup/single.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ const (
// implicitly denotes that this channel uses the new anchor commitment
// format.
AnchorsCommitVersion = 2

// AnchorsZeroFeeHtlcTxCommitVersion is a version that denotes this
// channel is using the zero-fee second-level anchor commitment format.
AnchorsZeroFeeHtlcTxCommitVersion = 3
)

// Single is a static description of an existing channel that can be used for
Expand Down Expand Up @@ -163,6 +167,9 @@ func NewSingle(channel *channeldb.OpenChannel,
}

switch {
case channel.ChanType.ZeroHtlcTxFee():
single.Version = AnchorsZeroFeeHtlcTxCommitVersion

case channel.ChanType.HasAnchors():
single.Version = AnchorsCommitVersion

Expand All @@ -185,6 +192,7 @@ func (s *Single) Serialize(w io.Writer) error {
case DefaultSingleVersion:
case TweaklessCommitVersion:
case AnchorsCommitVersion:
case AnchorsZeroFeeHtlcTxCommitVersion:
default:
return fmt.Errorf("unable to serialize w/ unknown "+
"version: %v", s.Version)
Expand Down Expand Up @@ -344,6 +352,7 @@ func (s *Single) Deserialize(r io.Reader) error {
case DefaultSingleVersion:
case TweaklessCommitVersion:
case AnchorsCommitVersion:
case AnchorsZeroFeeHtlcTxCommitVersion:
default:
return fmt.Errorf("unable to de-serialize w/ unknown "+
"version: %v", s.Version)
Expand Down
10 changes: 10 additions & 0 deletions channeldb/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,10 @@ const (
// that only the responder can decide to cooperatively close the
// channel.
FrozenBit ChannelType = 1 << 4

// ZeroHtlcTxFeeBit indicates that the channel should use zero-fee
// second-level HTLC transactions.
ZeroHtlcTxFeeBit ChannelType = 1 << 5
)

// IsSingleFunder returns true if the channel type if one of the known single
Expand Down Expand Up @@ -275,6 +279,12 @@ func (c ChannelType) HasAnchors() bool {
return c&AnchorOutputsBit == AnchorOutputsBit
}

// ZeroHtlcTxFee returns true if this channel type uses second-level HTLC
// transactions signed with zero-fee.
func (c ChannelType) ZeroHtlcTxFee() bool {
return c&ZeroHtlcTxFeeBit == ZeroHtlcTxFeeBit
}

// IsFrozen returns true if the channel is considered to be "frozen". A frozen
// channel means that only the responder can initiate a cooperative channel
// closure.
Expand Down
5 changes: 5 additions & 0 deletions chanrestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ func (c *chanDBRestorer) openChannelShell(backup chanbackup.Single) (
chanType = channeldb.AnchorOutputsBit
chanType |= channeldb.SingleFunderTweaklessBit

case chanbackup.AnchorsZeroFeeHtlcTxCommitVersion:
chanType = channeldb.ZeroHtlcTxFeeBit
chanType |= channeldb.AnchorOutputsBit
Comment thread
Roasbeef marked this conversation as resolved.
Outdated
chanType |= channeldb.SingleFunderTweaklessBit

default:
return nil, fmt.Errorf("unknown Single version: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion feature/default_sets.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ var defaultSetDesc = setDesc{
SetNodeAnn: {}, // N
SetInvoice: {}, // 9
},
lnwire.AnchorsOptional: {
lnwire.AnchorsZeroFeeHtlcTxOptional: {
Comment thread
Roasbeef marked this conversation as resolved.
Outdated
SetInit: {}, // I
SetNodeAnn: {}, // N
},
Expand Down
4 changes: 2 additions & 2 deletions feature/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
raw.Unset(lnwire.StaticRemoteKeyRequired)
}
if cfg.NoAnchors {
raw.Unset(lnwire.AnchorsOptional)
raw.Unset(lnwire.AnchorsRequired)
raw.Unset(lnwire.AnchorsZeroFeeHtlcTxOptional)
raw.Unset(lnwire.AnchorsZeroFeeHtlcTxRequired)
}
if cfg.NoWumbo {
raw.Unset(lnwire.WumboChannelsOptional)
Expand Down
36 changes: 17 additions & 19 deletions fundingmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -1137,20 +1137,21 @@ func (f *fundingManager) ProcessFundingMsg(msg lnwire.Message, peer lnpeer.Peer)
func commitmentType(localFeatures,
remoteFeatures *lnwire.FeatureVector) lnwallet.CommitmentType {

// If both peers are signalling support for anchor commitments, this
// implicitly mean we'll create the channel of this type. Note that
// this also enables tweakless commitments, as anchor commitments are
// always tweakless.
localAnchors := localFeatures.HasFeature(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

lnwire.AnchorsOptional,
// If both peers are signalling support for anchor commitments with
// zero-fee HTLC transactions, we'll use this type.
localZeroFee := localFeatures.HasFeature(
lnwire.AnchorsZeroFeeHtlcTxOptional,
)
remoteAnchors := remoteFeatures.HasFeature(
lnwire.AnchorsOptional,
remoteZeroFee := remoteFeatures.HasFeature(
lnwire.AnchorsZeroFeeHtlcTxOptional,
)
if localAnchors && remoteAnchors {
return lnwallet.CommitmentTypeAnchors
if localZeroFee && remoteZeroFee {
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.
localTweakless := localFeatures.HasFeature(
lnwire.StaticRemoteKeyOptional,
)
Expand Down Expand Up @@ -1306,10 +1307,9 @@ func (f *fundingManager) handleFundingOpen(peer lnpeer.Peer,
// responding side of a single funder workflow, we don't commit any
// funds to the channel ourselves.
//
// Before we init the channel, we'll also check to see if we've
// negotiated the new tweakless commitment format. This is only the
// case if *both* us and the remote peer are signaling the proper
// feature bit.
// Before we init the channel, we'll also check to see what commitment
// format we can use with this peer. This is dependent on *both* us and
// the remote peer are signaling the proper feature bit.
commitType := commitmentType(
peer.LocalFeatures(), peer.RemoteFeatures(),
)
Expand Down Expand Up @@ -3116,7 +3116,6 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
case chainreg.LitecoinChain:
ourDustLimit = chainreg.DefaultLitecoinDustLimit
}

fndgLog.Infof("Initiating fundingRequest(local_amt=%v "+
"(subtract_fees=%v), push_amt=%v, chain_hash=%v, peer=%x, "+
"dust_limit=%v, min_confs=%v)", localAmt, msg.subtractFees,
Expand Down Expand Up @@ -3185,10 +3184,9 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
// wallet doesn't have enough funds to commit to this channel, then the
// request will fail, and be aborted.
//
// Before we init the channel, we'll also check to see if we've
// negotiated the new tweakless commitment format. This is only the
// case if *both* us and the remote peer are signaling the proper
// feature bit.
// Before we init the channel, we'll also check to see what commitment
// format we can use with this peer. This is dependent on *both* us and
// the remote peer are signaling the proper feature bit.
commitType := commitmentType(
msg.peer.LocalFeatures(), msg.peer.RemoteFeatures(),
)
Expand Down
12 changes: 12 additions & 0 deletions lncfg/protocol.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// +build !rpctest

package lncfg

// ProtocolOptions is a struct that we use to be able to test backwards
Expand All @@ -17,10 +19,20 @@ type ProtocolOptions struct {
// (channels larger than 0.16 BTC) channels, which is the opposite of
// mini.
WumboChans bool `long:"wumbo-channels" description:"if set, then lnd will create and accept requests for channels larger chan 0.16 BTC"`

// NoAnchors should be set if we don't want to support opening or accepting
// channels having the anchor commitment type.
NoAnchors bool `long:"no-anchors" description:"disable support for anchor commitments"`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure how specific we want to be here, but does it make sense to clarify that this is anchor_zero_fee_htlc vs just anchor_outputs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think users interacting with this flag don't have to know about the difference.

}

// Wumbo returns true if lnd should permit the creation and acceptance of wumbo
// channels.
func (l *ProtocolOptions) Wumbo() bool {
return l.WumboChans
}

// NoAnchorCommitments returns true if we have disabled support for the anchor
// commitment type.
func (l *ProtocolOptions) NoAnchorCommitments() bool {
return l.NoAnchors
}
6 changes: 0 additions & 6 deletions lncfg/protocol_experimental_off.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,3 @@ package lncfg
// features that also require a build-tag to activate.
type ExperimentalProtocol struct {
}

// AnchorCommitments returns true if support for the anchor commitment type
// should be signaled.
func (l *ExperimentalProtocol) AnchorCommitments() bool {
return false
}
9 changes: 0 additions & 9 deletions lncfg/protocol_experimental_on.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,4 @@ package lncfg
// ExperimentalProtocol is a sub-config that houses any experimental protocol
// features that also require a build-tag to activate.
type ExperimentalProtocol struct {
// Anchors should be set if we want to support opening or accepting
// channels having the anchor commitment type.
Anchors bool `long:"anchors" description:"EXPERIMENTAL: enable experimental support for anchor commitments, won't work with watchtowers"`
}

// AnchorCommitments returns true if support for the anchor commitment type
// should be signaled.
func (l *ExperimentalProtocol) AnchorCommitments() bool {
return l.Anchors
}
38 changes: 38 additions & 0 deletions lncfg/protocol_rpctest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// +build rpctest

package lncfg

// ProtocolOptions is a struct that we use to be able to test backwards
// compatibility of protocol additions, while defaulting to the latest within
// lnd, or to enable experimental protocol changes.
type ProtocolOptions struct {
// LegacyProtocol is a sub-config that houses all the legacy protocol
// options. These are mostly used for integration tests as most modern
// nodes shuld always run with them on by default.
LegacyProtocol `group:"legacy" namespace:"legacy"`

// ExperimentalProtocol is a sub-config that houses any experimental
// protocol features that also require a build-tag to activate.
ExperimentalProtocol

// WumboChans should be set if we want to enable support for wumbo
// (channels larger than 0.16 BTC) channels, which is the opposite of
// mini.
WumboChans bool `long:"wumbo-channels" description:"if set, then lnd will create and accept requests for channels larger chan 0.16 BTC"`

// Anchors enables anchor commitments.
// TODO(halseth): transition itests to anchors instead!
Anchors bool `long:"anchors" description:"enable support for anchor commitments"`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

before this was NoAnchors, now it's back to Anchors?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So there's two different versions of the config now: one for the itests, and the one for the rest. This commit makes the default one have anchor on, while the itest one it's off by default.

}

// Wumbo returns true if lnd should permit the creation and acceptance of wumbo
// channels.
func (l *ProtocolOptions) Wumbo() bool {
return l.WumboChans
}

// NoAnchorCommitments returns true if we have disabled support for the anchor
// commitment type.
func (l *ProtocolOptions) NoAnchorCommitments() bool {
return !l.Anchors
}
12 changes: 12 additions & 0 deletions lnwallet/commitment.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,12 @@ func CommitWeight(chanType channeldb.ChannelType) int64 {
func HtlcTimeoutFee(chanType channeldb.ChannelType,
feePerKw chainfee.SatPerKWeight) btcutil.Amount {

// For zero-fee HTLC channels, this will always be zero, regardless of
// feerate.
if chanType.ZeroHtlcTxFee() {
return 0
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EZ 😎

Could also instead have the param be passed in (0 in this case) if we want to make it a constant so it can be modified easily later. Based on recent discussions though, it seems like the zero fee route may be the best way forward.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer zero, since that mitigates the fee leak completely.

}

if chanType.HasAnchors() {
return feePerKw.FeeForWeight(input.HtlcTimeoutWeightConfirmed)
}
Expand All @@ -290,6 +296,12 @@ func HtlcTimeoutFee(chanType channeldb.ChannelType,
func HtlcSuccessFee(chanType channeldb.ChannelType,
feePerKw chainfee.SatPerKWeight) btcutil.Amount {

// For zero-fee HTLC channels, this will always be zero, regardless of
// feerate.
if chanType.ZeroHtlcTxFee() {
return 0
}

if chanType.HasAnchors() {
return feePerKw.FeeForWeight(input.HtlcSuccessWeightConfirmed)
}
Expand Down
26 changes: 14 additions & 12 deletions lnwallet/reservation.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ const (
// to_remote key is static.
CommitmentTypeTweakless

// CommitmentTypeAnchors is a commitment type that is tweakless, and
// has extra anchor ouputs in order to bump the fee of the commitment
// transaction.
CommitmentTypeAnchors
// CommitmentTypeAnchorsZeroFeeHtlcTx is a commitment type that is an
// extension of the outdated CommitmentTypeAnchors, which in addition
// requires second-level HTLC transactions to be signed using a
// zero-fee.
CommitmentTypeAnchorsZeroFeeHtlcTx
)

// String returns the name of the CommitmentType.
Expand All @@ -41,8 +42,8 @@ func (c CommitmentType) String() string {
return "legacy"
case CommitmentTypeTweakless:
return "tweakless"
case CommitmentTypeAnchors:
return "anchors"
case CommitmentTypeAnchorsZeroFeeHtlcTx:
return "anchors-zero-fee-second-level"
default:
return "invalid"
}
Expand Down Expand Up @@ -182,7 +183,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
// Based on the channel type, we determine the initial commit weight
// and fee.
commitWeight := int64(input.CommitWeight)
if commitType == CommitmentTypeAnchors {
if commitType == CommitmentTypeAnchorsZeroFeeHtlcTx {
commitWeight = input.AnchorCommitWeight
}
commitFee := commitFeePerKw.FeeForWeight(commitWeight)
Expand All @@ -195,7 +196,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
// The total fee paid by the initiator will be the commitment fee in
// addition to the two anchor outputs.
feeMSat := lnwire.NewMSatFromSatoshis(commitFee)
if commitType == CommitmentTypeAnchors {
if commitType == CommitmentTypeAnchorsZeroFeeHtlcTx {
feeMSat += 2 * lnwire.NewMSatFromSatoshis(anchorSize)
}

Expand Down Expand Up @@ -280,8 +281,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
// Both the tweakless type and the anchor type is tweakless,
// hence set the bit.
if commitType == CommitmentTypeTweakless ||
commitType == CommitmentTypeAnchors {

commitType == CommitmentTypeAnchorsZeroFeeHtlcTx {
chanType |= channeldb.SingleFunderTweaklessBit
} else {
chanType |= channeldb.SingleFunderBit
Expand Down Expand Up @@ -315,9 +315,11 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
chanType |= channeldb.DualFunderBit
}

// We are adding anchor outputs to our commitment.
if commitType == CommitmentTypeAnchors {
// We are adding anchor outputs to our commitment. We only support this
// in combination with zero-fee second-levels HTLCs.
if commitType == CommitmentTypeAnchorsZeroFeeHtlcTx {
chanType |= channeldb.AnchorOutputsBit
chanType |= channeldb.ZeroHtlcTxFeeBit
}

// If the channel is meant to be frozen, then we'll set the frozen bit
Expand Down
12 changes: 12 additions & 0 deletions lnwire/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,16 @@ const (
// outputs.
AnchorsOptional FeatureBit = 21

// AnchorsZeroFeeHtlcTxRequired is a required feature bit that signals
// that the node requires channels having zero-fee second-level HTLC
// transactions, which also imply anchor commitments.
AnchorsZeroFeeHtlcTxRequired FeatureBit = 22

// AnchorsZeroFeeHtlcTxRequired is an optional feature bit that signals
// that the node supports channels having zero-fee second-level HTLC
// transactions, which also imply anchor commitments.
AnchorsZeroFeeHtlcTxOptional FeatureBit = 23

// maxAllowedSize is a maximum allowed size of feature vector.
//
// NOTE: Within the protocol, the maximum allowed message size is 65535
Expand Down Expand Up @@ -158,6 +168,8 @@ var Features = map[FeatureBit]string{
MPPRequired: "multi-path-payments",
AnchorsRequired: "anchor-commitments",
AnchorsOptional: "anchor-commitments",
AnchorsZeroFeeHtlcTxRequired: "anchors-zero-fee-htlc-tx",
AnchorsZeroFeeHtlcTxOptional: "anchors-zero-fee-htlc-tx",
WumboChannelsRequired: "wumbo-channels",
WumboChannelsOptional: "wumbo-channels",
}
Expand Down
4 changes: 2 additions & 2 deletions sample-lnd.conf
Original file line number Diff line number Diff line change
Expand Up @@ -938,8 +938,8 @@ litecoin.node=ltcd
; BTC
; protocol.wumbo-channels=true

; Set to enable experimental support for anchor commitments, won't work with watchtowers yet.
; protocol.anchors=true
; Set to disable support for anchor commitments
; protocol.no-anchors=true

[db]
; The selected database backend. The current default backend is "bolt". lnd
Expand Down
2 changes: 1 addition & 1 deletion server.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
featureMgr, err := feature.NewManager(feature.Config{
NoTLVOnion: cfg.ProtocolOptions.LegacyOnion(),
NoStaticRemoteKey: cfg.ProtocolOptions.NoStaticRemoteKey(),
NoAnchors: !cfg.ProtocolOptions.AnchorCommitments(),
NoAnchors: cfg.ProtocolOptions.NoAnchorCommitments(),
NoWumbo: !cfg.ProtocolOptions.Wumbo(),
})
if err != nil {
Expand Down