Skip to content

Commit

Permalink
Merge pull request #4840 from halseth/anchors-zero-fee-secondlevel
Browse files Browse the repository at this point in the history
[anchors] zero-fee HTLC secondlevel transactions
  • Loading branch information
Roasbeef authored Dec 15, 2020
2 parents f8dda6f + 64659e6 commit d289a6f
Show file tree
Hide file tree
Showing 15 changed files with 135 additions and 52 deletions.
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
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: {
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(
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"`
}

// 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"`
}

// 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
}

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

0 comments on commit d289a6f

Please sign in to comment.