Merge pull request #4840 from halseth/anchors-zero-fee-secondlevel
[anchors] zero-fee HTLC secondlevel transactions
This commit is contained in:
commit
d289a6ff78
@ -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
|
||||
@ -163,6 +167,9 @@ func NewSingle(channel *channeldb.OpenChannel,
|
||||
}
|
||||
|
||||
switch {
|
||||
case channel.ChanType.ZeroHtlcTxFee():
|
||||
single.Version = AnchorsZeroFeeHtlcTxCommitVersion
|
||||
|
||||
case channel.ChanType.HasAnchors():
|
||||
single.Version = AnchorsCommitVersion
|
||||
|
||||
@ -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)
|
||||
@ -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)
|
||||
|
@ -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
|
||||
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ var defaultSetDesc = setDesc{
|
||||
SetNodeAnn: {}, // N
|
||||
SetInvoice: {}, // 9
|
||||
},
|
||||
lnwire.AnchorsOptional: {
|
||||
lnwire.AnchorsZeroFeeHtlcTxOptional: {
|
||||
SetInit: {}, // I
|
||||
SetNodeAnn: {}, // N
|
||||
},
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
)
|
||||
@ -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(),
|
||||
)
|
||||
@ -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,
|
||||
@ -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(),
|
||||
)
|
||||
|
@ -1,3 +1,5 @@
|
||||
// +build !rpctest
|
||||
|
||||
package lncfg
|
||||
|
||||
// ProtocolOptions is a struct that we use to be able to test backwards
|
||||
@ -17,6 +19,10 @@ 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
|
||||
@ -24,3 +30,9 @@ type ProtocolOptions struct {
|
||||
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,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
|
||||
}
|
||||
|
@ -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
lncfg/protocol_rpctest.go
Normal file
38
lncfg/protocol_rpctest.go
Normal file
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
|
@ -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.
|
||||
@ -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"
|
||||
}
|
||||
@ -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)
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
|
@ -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
|
||||
@ -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",
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user