mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-22 06:21:40 +01:00
funding+lnwallet: support funding new script enforced leased channels
This commit is contained in:
parent
f38c6d6662
commit
564ec0fd9b
4 changed files with 128 additions and 14 deletions
|
@ -50,6 +50,23 @@ func explicitNegotiateCommitmentType(channelType lnwire.ChannelType,
|
||||||
channelFeatures := lnwire.RawFeatureVector(channelType)
|
channelFeatures := lnwire.RawFeatureVector(channelType)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
// Lease script enforcement + anchors zero fee + static remote key
|
||||||
|
// features only.
|
||||||
|
case channelFeatures.OnlyContains(
|
||||||
|
lnwire.ScriptEnforcedLeaseRequired,
|
||||||
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
|
lnwire.StaticRemoteKeyRequired,
|
||||||
|
):
|
||||||
|
if !hasFeatures(
|
||||||
|
local, remote,
|
||||||
|
lnwire.ScriptEnforcedLeaseOptional,
|
||||||
|
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||||
|
lnwire.StaticRemoteKeyOptional,
|
||||||
|
) {
|
||||||
|
return 0, errUnsupportedChannelType
|
||||||
|
}
|
||||||
|
return lnwallet.CommitmentTypeScriptEnforcedLease, nil
|
||||||
|
|
||||||
// Anchors zero fee + static remote key features only.
|
// Anchors zero fee + static remote key features only.
|
||||||
case channelFeatures.OnlyContains(
|
case channelFeatures.OnlyContains(
|
||||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
|
|
|
@ -1364,6 +1364,28 @@ func (f *Manager) handleFundingOpen(peer lnpeer.Peer,
|
||||||
}
|
}
|
||||||
reservation.SetOurUpfrontShutdown(shutdown)
|
reservation.SetOurUpfrontShutdown(shutdown)
|
||||||
|
|
||||||
|
// If a script enforced channel lease is being proposed, we'll need to
|
||||||
|
// validate its custom TLV records.
|
||||||
|
if commitType == lnwallet.CommitmentTypeScriptEnforcedLease {
|
||||||
|
if msg.LeaseExpiry == nil {
|
||||||
|
err := errors.New("missing lease expiry")
|
||||||
|
f.failFundingFlow(peer, msg.PendingChannelID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we had a shim registered for this channel prior to
|
||||||
|
// receiving its corresponding OpenChannel message, then we'll
|
||||||
|
// validate the proposed LeaseExpiry against what was registered
|
||||||
|
// in our shim.
|
||||||
|
if reservation.LeaseExpiry() != 0 {
|
||||||
|
if uint32(*msg.LeaseExpiry) != reservation.LeaseExpiry() {
|
||||||
|
err := errors.New("lease expiry mismatch")
|
||||||
|
f.failFundingFlow(peer, msg.PendingChannelID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log.Infof("Requiring %v confirmations for pendingChan(%x): "+
|
log.Infof("Requiring %v confirmations for pendingChan(%x): "+
|
||||||
"amt=%v, push_amt=%v, committype=%v, upfrontShutdown=%x", numConfsReq,
|
"amt=%v, push_amt=%v, committype=%v, upfrontShutdown=%x", numConfsReq,
|
||||||
msg.PendingChannelID, amt, msg.PushAmount,
|
msg.PendingChannelID, amt, msg.PushAmount,
|
||||||
|
@ -1499,6 +1521,7 @@ func (f *Manager) handleFundingOpen(peer lnpeer.Peer,
|
||||||
FirstCommitmentPoint: ourContribution.FirstCommitmentPoint,
|
FirstCommitmentPoint: ourContribution.FirstCommitmentPoint,
|
||||||
UpfrontShutdownScript: ourContribution.UpfrontShutdown,
|
UpfrontShutdownScript: ourContribution.UpfrontShutdown,
|
||||||
ChannelType: msg.ChannelType,
|
ChannelType: msg.ChannelType,
|
||||||
|
LeaseExpiry: msg.LeaseExpiry,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := peer.SendMessage(true, &fundingAccept); err != nil {
|
if err := peer.SendMessage(true, &fundingAccept); err != nil {
|
||||||
|
@ -1530,11 +1553,12 @@ func (f *Manager) handleFundingAccept(peer lnpeer.Peer,
|
||||||
log.Infof("Recv'd fundingResponse for pending_id(%x)",
|
log.Infof("Recv'd fundingResponse for pending_id(%x)",
|
||||||
pendingChanID[:])
|
pendingChanID[:])
|
||||||
|
|
||||||
// We'll want to quickly check that ChannelType echoed by the channel
|
// Perform some basic validation of any custom TLV records included.
|
||||||
// request recipient matches what we proposed.
|
|
||||||
//
|
//
|
||||||
// TODO: Return errors as funding.Error to give context to remote peer?
|
// TODO: Return errors as funding.Error to give context to remote peer?
|
||||||
if resCtx.channelType != nil {
|
if resCtx.channelType != nil {
|
||||||
|
// We'll want to quickly check that the ChannelType echoed by
|
||||||
|
// the channel request recipient matches what we proposed.
|
||||||
if msg.ChannelType == nil {
|
if msg.ChannelType == nil {
|
||||||
err := errors.New("explicit channel type not echoed back")
|
err := errors.New("explicit channel type not echoed back")
|
||||||
f.failFundingFlow(peer, msg.PendingChannelID, err)
|
f.failFundingFlow(peer, msg.PendingChannelID, err)
|
||||||
|
@ -1547,6 +1571,21 @@ func (f *Manager) handleFundingAccept(peer lnpeer.Peer,
|
||||||
f.failFundingFlow(peer, msg.PendingChannelID, err)
|
f.failFundingFlow(peer, msg.PendingChannelID, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We'll want to do the same with the LeaseExpiry if one should
|
||||||
|
// be set.
|
||||||
|
if resCtx.reservation.LeaseExpiry() != 0 {
|
||||||
|
if msg.LeaseExpiry == nil {
|
||||||
|
err := errors.New("lease expiry not echoed back")
|
||||||
|
f.failFundingFlow(peer, msg.PendingChannelID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if uint32(*msg.LeaseExpiry) != resCtx.reservation.LeaseExpiry() {
|
||||||
|
err := errors.New("lease expiry mismatch")
|
||||||
|
f.failFundingFlow(peer, msg.PendingChannelID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if msg.ChannelType != nil {
|
} else if msg.ChannelType != nil {
|
||||||
err := errors.New("received unexpected channel type")
|
err := errors.New("received unexpected channel type")
|
||||||
f.failFundingFlow(peer, msg.PendingChannelID, err)
|
f.failFundingFlow(peer, msg.PendingChannelID, err)
|
||||||
|
@ -3206,9 +3245,8 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) {
|
||||||
|
|
||||||
// For anchor channels cap the initial commit fee rate at our defined
|
// For anchor channels cap the initial commit fee rate at our defined
|
||||||
// maximum.
|
// maximum.
|
||||||
if commitType == lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx &&
|
if commitType.HasAnchors() &&
|
||||||
commitFeePerKw > f.cfg.MaxAnchorsCommitFeeRate {
|
commitFeePerKw > f.cfg.MaxAnchorsCommitFeeRate {
|
||||||
|
|
||||||
commitFeePerKw = f.cfg.MaxAnchorsCommitFeeRate
|
commitFeePerKw = f.cfg.MaxAnchorsCommitFeeRate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3311,6 +3349,14 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) {
|
||||||
// remote party.
|
// remote party.
|
||||||
chanReserve := f.cfg.RequiredRemoteChanReserve(capacity, ourDustLimit)
|
chanReserve := f.cfg.RequiredRemoteChanReserve(capacity, ourDustLimit)
|
||||||
|
|
||||||
|
// When opening a script enforced channel lease, include the required
|
||||||
|
// expiry TLV record in our proposal.
|
||||||
|
var leaseExpiry *lnwire.LeaseExpiry
|
||||||
|
if commitType == lnwallet.CommitmentTypeScriptEnforcedLease {
|
||||||
|
leaseExpiry = new(lnwire.LeaseExpiry)
|
||||||
|
*leaseExpiry = lnwire.LeaseExpiry(reservation.LeaseExpiry())
|
||||||
|
}
|
||||||
|
|
||||||
log.Infof("Starting funding workflow with %v for pending_id(%x), "+
|
log.Infof("Starting funding workflow with %v for pending_id(%x), "+
|
||||||
"committype=%v", msg.Peer.Address(), chanID, commitType)
|
"committype=%v", msg.Peer.Address(), chanID, commitType)
|
||||||
|
|
||||||
|
@ -3335,6 +3381,7 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) {
|
||||||
ChannelFlags: channelFlags,
|
ChannelFlags: channelFlags,
|
||||||
UpfrontShutdownScript: shutdown,
|
UpfrontShutdownScript: shutdown,
|
||||||
ChannelType: msg.ChannelType,
|
ChannelType: msg.ChannelType,
|
||||||
|
LeaseExpiry: leaseExpiry,
|
||||||
}
|
}
|
||||||
if err := msg.Peer.SendMessage(true, &fundingOpen); err != nil {
|
if err := msg.Peer.SendMessage(true, &fundingOpen); err != nil {
|
||||||
e := fmt.Errorf("unable to send funding request message: %v",
|
e := fmt.Errorf("unable to send funding request message: %v",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package lnwallet
|
package lnwallet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -34,8 +35,40 @@ const (
|
||||||
// requires second-level HTLC transactions to be signed using a
|
// requires second-level HTLC transactions to be signed using a
|
||||||
// zero-fee.
|
// zero-fee.
|
||||||
CommitmentTypeAnchorsZeroFeeHtlcTx
|
CommitmentTypeAnchorsZeroFeeHtlcTx
|
||||||
|
|
||||||
|
// CommitmentTypeScriptEnforcedLease is a commitment type that builds
|
||||||
|
// upon CommitmentTypeTweakless and CommitmentTypeAnchorsZeroFeeHtlcTx,
|
||||||
|
// which in addition requires a CLTV clause to spend outputs paying to
|
||||||
|
// the channel initiator. This is intended for use on leased channels to
|
||||||
|
// guarantee that the channel initiator has no incentives to close a
|
||||||
|
// leased channel before its maturity date.
|
||||||
|
CommitmentTypeScriptEnforcedLease
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// HasStaticRemoteKey returns whether the commitment type supports remote
|
||||||
|
// outputs backed by static keys.
|
||||||
|
func (c CommitmentType) HasStaticRemoteKey() bool {
|
||||||
|
switch c {
|
||||||
|
case CommitmentTypeTweakless,
|
||||||
|
CommitmentTypeAnchorsZeroFeeHtlcTx,
|
||||||
|
CommitmentTypeScriptEnforcedLease:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasAnchors returns whether the commitment type supports anchor outputs.
|
||||||
|
func (c CommitmentType) HasAnchors() bool {
|
||||||
|
switch c {
|
||||||
|
case CommitmentTypeAnchorsZeroFeeHtlcTx,
|
||||||
|
CommitmentTypeScriptEnforcedLease:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// String returns the name of the CommitmentType.
|
// String returns the name of the CommitmentType.
|
||||||
func (c CommitmentType) String() string {
|
func (c CommitmentType) String() string {
|
||||||
switch c {
|
switch c {
|
||||||
|
@ -45,6 +78,8 @@ func (c CommitmentType) String() string {
|
||||||
return "tweakless"
|
return "tweakless"
|
||||||
case CommitmentTypeAnchorsZeroFeeHtlcTx:
|
case CommitmentTypeAnchorsZeroFeeHtlcTx:
|
||||||
return "anchors-zero-fee-second-level"
|
return "anchors-zero-fee-second-level"
|
||||||
|
case CommitmentTypeScriptEnforcedLease:
|
||||||
|
return "script-enforced-lease"
|
||||||
default:
|
default:
|
||||||
return "invalid"
|
return "invalid"
|
||||||
}
|
}
|
||||||
|
@ -188,8 +223,8 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
|
||||||
// Based on the channel type, we determine the initial commit weight
|
// Based on the channel type, we determine the initial commit weight
|
||||||
// and fee.
|
// and fee.
|
||||||
commitWeight := int64(input.CommitWeight)
|
commitWeight := int64(input.CommitWeight)
|
||||||
if commitType == CommitmentTypeAnchorsZeroFeeHtlcTx {
|
if commitType.HasAnchors() {
|
||||||
commitWeight = input.AnchorCommitWeight
|
commitWeight = int64(input.AnchorCommitWeight)
|
||||||
}
|
}
|
||||||
commitFee := commitFeePerKw.FeeForWeight(commitWeight)
|
commitFee := commitFeePerKw.FeeForWeight(commitWeight)
|
||||||
|
|
||||||
|
@ -201,7 +236,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
|
||||||
// The total fee paid by the initiator will be the commitment fee in
|
// The total fee paid by the initiator will be the commitment fee in
|
||||||
// addition to the two anchor outputs.
|
// addition to the two anchor outputs.
|
||||||
feeMSat := lnwire.NewMSatFromSatoshis(commitFee)
|
feeMSat := lnwire.NewMSatFromSatoshis(commitFee)
|
||||||
if commitType == CommitmentTypeAnchorsZeroFeeHtlcTx {
|
if commitType.HasAnchors() {
|
||||||
feeMSat += 2 * lnwire.NewMSatFromSatoshis(anchorSize)
|
feeMSat += 2 * lnwire.NewMSatFromSatoshis(anchorSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,8 +323,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
|
||||||
if ourBalance == 0 || theirBalance == 0 || pushMSat != 0 {
|
if ourBalance == 0 || theirBalance == 0 || pushMSat != 0 {
|
||||||
// Both the tweakless type and the anchor type is tweakless,
|
// Both the tweakless type and the anchor type is tweakless,
|
||||||
// hence set the bit.
|
// hence set the bit.
|
||||||
if commitType == CommitmentTypeTweakless ||
|
if commitType.HasStaticRemoteKey() {
|
||||||
commitType == CommitmentTypeAnchorsZeroFeeHtlcTx {
|
|
||||||
chanType |= channeldb.SingleFunderTweaklessBit
|
chanType |= channeldb.SingleFunderTweaklessBit
|
||||||
} else {
|
} else {
|
||||||
chanType |= channeldb.SingleFunderBit
|
chanType |= channeldb.SingleFunderBit
|
||||||
|
@ -325,14 +359,20 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
|
||||||
|
|
||||||
// We are adding anchor outputs to our commitment. We only support this
|
// We are adding anchor outputs to our commitment. We only support this
|
||||||
// in combination with zero-fee second-levels HTLCs.
|
// in combination with zero-fee second-levels HTLCs.
|
||||||
if commitType == CommitmentTypeAnchorsZeroFeeHtlcTx {
|
if commitType.HasAnchors() {
|
||||||
chanType |= channeldb.AnchorOutputsBit
|
chanType |= channeldb.AnchorOutputsBit
|
||||||
chanType |= channeldb.ZeroHtlcTxFeeBit
|
chanType |= channeldb.ZeroHtlcTxFeeBit
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the channel is meant to be frozen, then we'll set the frozen bit
|
// Set the appropriate LeaseExpiration/Frozen bit based on the
|
||||||
// now so once the channel is open, it can be interpreted properly.
|
// reservation parameters.
|
||||||
if thawHeight != 0 {
|
if commitType == CommitmentTypeScriptEnforcedLease {
|
||||||
|
if thawHeight == 0 {
|
||||||
|
return nil, errors.New("missing absolute expiration " +
|
||||||
|
"for script enforced lease commitment type")
|
||||||
|
}
|
||||||
|
chanType |= channeldb.LeaseExpirationBit
|
||||||
|
} else if thawHeight > 0 {
|
||||||
chanType |= channeldb.FrozenBit
|
chanType |= channeldb.FrozenBit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -719,6 +759,16 @@ func (r *ChannelReservation) Capacity() btcutil.Amount {
|
||||||
return r.partialState.Capacity
|
return r.partialState.Capacity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LeaseExpiry returns the absolute expiration height for a leased channel using
|
||||||
|
// the script enforced commitment type. A zero value is returned when the
|
||||||
|
// channel is not using a script enforced lease commitment type.
|
||||||
|
func (r *ChannelReservation) LeaseExpiry() uint32 {
|
||||||
|
if !r.partialState.ChanType.HasLeaseExpiration() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return r.partialState.ThawHeight
|
||||||
|
}
|
||||||
|
|
||||||
// Cancel abandons this channel reservation. This method should be called in
|
// Cancel abandons this channel reservation. This method should be called in
|
||||||
// the scenario that communications with the counterparty break down. Upon
|
// the scenario that communications with the counterparty break down. Upon
|
||||||
// cancellation, all resources previously reserved for this pending payment
|
// cancellation, all resources previously reserved for this pending payment
|
||||||
|
|
|
@ -818,7 +818,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg
|
||||||
// funding tx ready, so this will always pass. We'll do another check
|
// funding tx ready, so this will always pass. We'll do another check
|
||||||
// when the PSBT has been verified.
|
// when the PSBT has been verified.
|
||||||
isPublic := req.Flags&lnwire.FFAnnounceChannel != 0
|
isPublic := req.Flags&lnwire.FFAnnounceChannel != 0
|
||||||
hasAnchors := req.CommitType == CommitmentTypeAnchorsZeroFeeHtlcTx
|
hasAnchors := req.CommitType.HasAnchors()
|
||||||
err = l.enforceNewReservedValue(fundingIntent, isPublic, hasAnchors)
|
err = l.enforceNewReservedValue(fundingIntent, isPublic, hasAnchors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fundingIntent.Cancel()
|
fundingIntent.Cancel()
|
||||||
|
|
Loading…
Add table
Reference in a new issue