mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-03 03:29:50 +02:00
Merge pull request #7177 from morehouse/fix_chantype_negotiation
funding: fix channel type negotiation bug
This commit is contained in:
commit
c6a68d193c
@ -148,6 +148,9 @@
|
|||||||
* [Cancel rebroadcasting of a transaction when abandoning
|
* [Cancel rebroadcasting of a transaction when abandoning
|
||||||
a channel](https://github.com/lightningnetwork/lnd/pull/7819).
|
a channel](https://github.com/lightningnetwork/lnd/pull/7819).
|
||||||
|
|
||||||
|
* [Fixed a validation bug](https://github.com/lightningnetwork/lnd/pull/7177) in
|
||||||
|
`channel_type` negotiation.
|
||||||
|
|
||||||
## Code Health
|
## Code Health
|
||||||
|
|
||||||
* Updated [our fork for serializing protobuf as JSON to be based on the
|
* Updated [our fork for serializing protobuf as JSON to be based on the
|
||||||
|
@ -8,12 +8,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
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
|
// errUnsupportedCommitmentType is an error returned when a specific
|
||||||
// channel commitment type is being explicitly negotiated but either
|
// channel commitment type is being explicitly negotiated but either
|
||||||
// peer of the channel does not support it.
|
// peer of the channel does not support it.
|
||||||
@ -22,40 +16,63 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// negotiateCommitmentType negotiates the commitment type of a newly opened
|
// negotiateCommitmentType negotiates the commitment type of a newly opened
|
||||||
// channel. If a channelType is provided, explicit negotiation for said type
|
// channel. If a desiredChanType is provided, explicit negotiation for said type
|
||||||
// will be attempted if the set of both local and remote features support it.
|
// will be attempted if the set of both local and remote features support it.
|
||||||
// Otherwise, implicit negotiation will be attempted. Two booleans are
|
// Otherwise, implicit negotiation will be attempted.
|
||||||
// returned letting the caller know if the option-scid-alias or zero-conf
|
//
|
||||||
// channel types were negotiated.
|
// The returned ChannelType is nil when implicit negotiation is used. An error
|
||||||
func negotiateCommitmentType(channelType *lnwire.ChannelType, local,
|
// is only returned if desiredChanType is not supported.
|
||||||
remote *lnwire.FeatureVector, mustBeExplicit bool) (bool,
|
func negotiateCommitmentType(desiredChanType *lnwire.ChannelType, local,
|
||||||
*lnwire.ChannelType, lnwallet.CommitmentType, error) {
|
remote *lnwire.FeatureVector) (*lnwire.ChannelType,
|
||||||
|
lnwallet.CommitmentType, error) {
|
||||||
|
|
||||||
if channelType != nil {
|
// BOLT#2 specifies we MUST use explicit negotiation if both peers
|
||||||
// If the peer does know explicit negotiation, let's attempt
|
// signal for it.
|
||||||
// that now.
|
explicitNegotiation := hasFeatures(
|
||||||
if hasFeatures(
|
local, remote, lnwire.ExplicitChannelTypeOptional,
|
||||||
local, remote, lnwire.ExplicitChannelTypeOptional,
|
)
|
||||||
) {
|
|
||||||
|
|
||||||
chanType, err := explicitNegotiateCommitmentType(
|
chanTypeRequested := desiredChanType != nil
|
||||||
*channelType, local, remote,
|
|
||||||
)
|
switch {
|
||||||
return true, channelType, chanType, err
|
case explicitNegotiation && chanTypeRequested:
|
||||||
|
commitType, err := explicitNegotiateCommitmentType(
|
||||||
|
*desiredChanType, local, remote,
|
||||||
|
)
|
||||||
|
|
||||||
|
return desiredChanType, commitType, err
|
||||||
|
|
||||||
|
// We don't have a specific channel type requested, so we select a
|
||||||
|
// default type as if implicit negotiation were used, and then we
|
||||||
|
// explicitly signal that default type.
|
||||||
|
case explicitNegotiation && !chanTypeRequested:
|
||||||
|
defaultChanType, commitType := implicitNegotiateCommitmentType(
|
||||||
|
local, remote,
|
||||||
|
)
|
||||||
|
|
||||||
|
return defaultChanType, commitType, nil
|
||||||
|
|
||||||
|
// A specific channel type was requested, but we can't explicitly signal
|
||||||
|
// it. So if implicit negotiation wouldn't select the desired channel
|
||||||
|
// type, we must return an error.
|
||||||
|
case !explicitNegotiation && chanTypeRequested:
|
||||||
|
implicitChanType, commitType := implicitNegotiateCommitmentType(
|
||||||
|
local, remote,
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := lnwire.RawFeatureVector(*desiredChanType)
|
||||||
|
actual := lnwire.RawFeatureVector(*implicitChanType)
|
||||||
|
if !expected.Equals(&actual) {
|
||||||
|
return nil, 0, errUnsupportedChannelType
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're the funder, and we are attempting to use an
|
return nil, commitType, nil
|
||||||
// explicit channel type, but the remote party doesn't signal
|
|
||||||
// the bit, then we actually want to exit here, to ensure the
|
default: // !explicitNegotiation && !chanTypeRequested
|
||||||
// user doesn't end up with an unexpected channel type via
|
_, commitType := implicitNegotiateCommitmentType(local, remote)
|
||||||
// implicit negotiation.
|
|
||||||
if mustBeExplicit {
|
return nil, commitType, nil
|
||||||
return false, nil, 0, errUnsupportedExplicitNegotiation
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chanType, commitType := implicitNegotiateCommitmentType(local, remote)
|
|
||||||
return false, chanType, commitType, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// explicitNegotiateCommitmentType attempts to explicitly negotiate for a
|
// explicitNegotiateCommitmentType attempts to explicitly negotiate for a
|
||||||
|
@ -15,12 +15,11 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
|||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
mustBeExplicit bool
|
|
||||||
channelFeatures *lnwire.RawFeatureVector
|
channelFeatures *lnwire.RawFeatureVector
|
||||||
localFeatures *lnwire.RawFeatureVector
|
localFeatures *lnwire.RawFeatureVector
|
||||||
remoteFeatures *lnwire.RawFeatureVector
|
remoteFeatures *lnwire.RawFeatureVector
|
||||||
expectsCommitType lnwallet.CommitmentType
|
expectsCommitType lnwallet.CommitmentType
|
||||||
expectsChanType lnwire.ChannelType
|
expectsChanType *lnwire.ChannelType
|
||||||
zeroConf bool
|
zeroConf bool
|
||||||
scidAlias bool
|
scidAlias bool
|
||||||
expectsErr error
|
expectsErr error
|
||||||
@ -41,30 +40,8 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
|||||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||||
),
|
),
|
||||||
expectsCommitType: lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx,
|
expectsCommitType: lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx,
|
||||||
expectsChanType: lnwire.ChannelType(*lnwire.NewRawFeatureVector(
|
expectsChanType: nil,
|
||||||
lnwire.StaticRemoteKeyRequired,
|
expectsErr: nil,
|
||||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
|
||||||
)),
|
|
||||||
expectsErr: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "local funder wants explicit, remote doesn't " +
|
|
||||||
"support so fall back",
|
|
||||||
mustBeExplicit: true,
|
|
||||||
channelFeatures: lnwire.NewRawFeatureVector(
|
|
||||||
lnwire.StaticRemoteKeyRequired,
|
|
||||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
|
||||||
),
|
|
||||||
localFeatures: lnwire.NewRawFeatureVector(
|
|
||||||
lnwire.StaticRemoteKeyOptional,
|
|
||||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
|
||||||
lnwire.ExplicitChannelTypeOptional,
|
|
||||||
),
|
|
||||||
remoteFeatures: lnwire.NewRawFeatureVector(
|
|
||||||
lnwire.StaticRemoteKeyOptional,
|
|
||||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
|
||||||
),
|
|
||||||
expectsErr: errUnsupportedExplicitNegotiation,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "explicit missing remote commitment feature",
|
name: "explicit missing remote commitment feature",
|
||||||
@ -106,8 +83,8 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
|||||||
lnwire.ExplicitChannelTypeOptional,
|
lnwire.ExplicitChannelTypeOptional,
|
||||||
),
|
),
|
||||||
expectsCommitType: lnwallet.CommitmentTypeScriptEnforcedLease,
|
expectsCommitType: lnwallet.CommitmentTypeScriptEnforcedLease,
|
||||||
expectsChanType: lnwire.ChannelType(
|
expectsChanType: (*lnwire.ChannelType)(
|
||||||
*lnwire.NewRawFeatureVector(
|
lnwire.NewRawFeatureVector(
|
||||||
lnwire.ZeroConfRequired,
|
lnwire.ZeroConfRequired,
|
||||||
lnwire.StaticRemoteKeyRequired,
|
lnwire.StaticRemoteKeyRequired,
|
||||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
@ -137,8 +114,8 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
|||||||
lnwire.ExplicitChannelTypeOptional,
|
lnwire.ExplicitChannelTypeOptional,
|
||||||
),
|
),
|
||||||
expectsCommitType: lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx,
|
expectsCommitType: lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx,
|
||||||
expectsChanType: lnwire.ChannelType(
|
expectsChanType: (*lnwire.ChannelType)(
|
||||||
*lnwire.NewRawFeatureVector(
|
lnwire.NewRawFeatureVector(
|
||||||
lnwire.ZeroConfRequired,
|
lnwire.ZeroConfRequired,
|
||||||
lnwire.StaticRemoteKeyRequired,
|
lnwire.StaticRemoteKeyRequired,
|
||||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
@ -170,8 +147,8 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
|||||||
lnwire.ExplicitChannelTypeOptional,
|
lnwire.ExplicitChannelTypeOptional,
|
||||||
),
|
),
|
||||||
expectsCommitType: lnwallet.CommitmentTypeScriptEnforcedLease,
|
expectsCommitType: lnwallet.CommitmentTypeScriptEnforcedLease,
|
||||||
expectsChanType: lnwire.ChannelType(
|
expectsChanType: (*lnwire.ChannelType)(
|
||||||
*lnwire.NewRawFeatureVector(
|
lnwire.NewRawFeatureVector(
|
||||||
lnwire.ScidAliasRequired,
|
lnwire.ScidAliasRequired,
|
||||||
lnwire.StaticRemoteKeyRequired,
|
lnwire.StaticRemoteKeyRequired,
|
||||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
@ -201,8 +178,8 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
|||||||
lnwire.ExplicitChannelTypeOptional,
|
lnwire.ExplicitChannelTypeOptional,
|
||||||
),
|
),
|
||||||
expectsCommitType: lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx,
|
expectsCommitType: lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx,
|
||||||
expectsChanType: lnwire.ChannelType(
|
expectsChanType: (*lnwire.ChannelType)(
|
||||||
*lnwire.NewRawFeatureVector(
|
lnwire.NewRawFeatureVector(
|
||||||
lnwire.ScidAliasRequired,
|
lnwire.ScidAliasRequired,
|
||||||
lnwire.StaticRemoteKeyRequired,
|
lnwire.StaticRemoteKeyRequired,
|
||||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
@ -228,10 +205,12 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
|||||||
lnwire.ExplicitChannelTypeOptional,
|
lnwire.ExplicitChannelTypeOptional,
|
||||||
),
|
),
|
||||||
expectsCommitType: lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx,
|
expectsCommitType: lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx,
|
||||||
expectsChanType: lnwire.ChannelType(*lnwire.NewRawFeatureVector(
|
expectsChanType: (*lnwire.ChannelType)(
|
||||||
lnwire.StaticRemoteKeyRequired,
|
lnwire.NewRawFeatureVector(
|
||||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
lnwire.StaticRemoteKeyRequired,
|
||||||
)),
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
|
),
|
||||||
|
),
|
||||||
expectsErr: nil,
|
expectsErr: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -250,9 +229,11 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
|||||||
lnwire.ExplicitChannelTypeOptional,
|
lnwire.ExplicitChannelTypeOptional,
|
||||||
),
|
),
|
||||||
expectsCommitType: lnwallet.CommitmentTypeTweakless,
|
expectsCommitType: lnwallet.CommitmentTypeTweakless,
|
||||||
expectsChanType: lnwire.ChannelType(*lnwire.NewRawFeatureVector(
|
expectsChanType: (*lnwire.ChannelType)(
|
||||||
lnwire.StaticRemoteKeyRequired,
|
lnwire.NewRawFeatureVector(
|
||||||
)),
|
lnwire.StaticRemoteKeyRequired,
|
||||||
|
),
|
||||||
|
),
|
||||||
expectsErr: nil,
|
expectsErr: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -269,14 +250,16 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
|||||||
lnwire.ExplicitChannelTypeOptional,
|
lnwire.ExplicitChannelTypeOptional,
|
||||||
),
|
),
|
||||||
expectsCommitType: lnwallet.CommitmentTypeLegacy,
|
expectsCommitType: lnwallet.CommitmentTypeLegacy,
|
||||||
expectsChanType: lnwire.ChannelType(*lnwire.NewRawFeatureVector()),
|
expectsChanType: (*lnwire.ChannelType)(
|
||||||
expectsErr: nil,
|
lnwire.NewRawFeatureVector(),
|
||||||
|
),
|
||||||
|
expectsErr: nil,
|
||||||
},
|
},
|
||||||
// Both sides signal the explicit chan type bit, so we expect
|
// Both sides signal the explicit chan type bit, so we expect
|
||||||
// that we return the corresponding chan type feature bits,
|
// that we return the corresponding chan type feature bits,
|
||||||
// even though we didn't set an explicit channel type.
|
// even though we didn't set a desired channel type.
|
||||||
{
|
{
|
||||||
name: "implicit anchors",
|
name: "default explicit anchors",
|
||||||
channelFeatures: nil,
|
channelFeatures: nil,
|
||||||
localFeatures: lnwire.NewRawFeatureVector(
|
localFeatures: lnwire.NewRawFeatureVector(
|
||||||
lnwire.StaticRemoteKeyOptional,
|
lnwire.StaticRemoteKeyOptional,
|
||||||
@ -289,10 +272,12 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
|||||||
lnwire.ExplicitChannelTypeOptional,
|
lnwire.ExplicitChannelTypeOptional,
|
||||||
),
|
),
|
||||||
expectsCommitType: lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx,
|
expectsCommitType: lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx,
|
||||||
expectsChanType: lnwire.ChannelType(*lnwire.NewRawFeatureVector(
|
expectsChanType: (*lnwire.ChannelType)(
|
||||||
lnwire.StaticRemoteKeyRequired,
|
lnwire.NewRawFeatureVector(
|
||||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
lnwire.StaticRemoteKeyRequired,
|
||||||
)),
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
|
),
|
||||||
|
),
|
||||||
expectsErr: nil,
|
expectsErr: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -306,10 +291,8 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
|||||||
lnwire.StaticRemoteKeyOptional,
|
lnwire.StaticRemoteKeyOptional,
|
||||||
),
|
),
|
||||||
expectsCommitType: lnwallet.CommitmentTypeTweakless,
|
expectsCommitType: lnwallet.CommitmentTypeTweakless,
|
||||||
expectsChanType: lnwire.ChannelType(*lnwire.NewRawFeatureVector(
|
expectsChanType: nil,
|
||||||
lnwire.StaticRemoteKeyRequired,
|
expectsErr: nil,
|
||||||
)),
|
|
||||||
expectsErr: nil,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "implicit legacy",
|
name: "implicit legacy",
|
||||||
@ -320,7 +303,7 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
|||||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||||
),
|
),
|
||||||
expectsCommitType: lnwallet.CommitmentTypeLegacy,
|
expectsCommitType: lnwallet.CommitmentTypeLegacy,
|
||||||
expectsChanType: lnwire.ChannelType(*lnwire.NewRawFeatureVector()),
|
expectsChanType: nil,
|
||||||
expectsErr: nil,
|
expectsErr: nil,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -343,9 +326,8 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, lChan, lCommit, err := negotiateCommitmentType(
|
lChan, lCommit, err := negotiateCommitmentType(
|
||||||
channelType, localFeatures, remoteFeatures,
|
channelType, localFeatures, remoteFeatures,
|
||||||
testCase.mustBeExplicit,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -369,9 +351,8 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
|||||||
require.Equal(t, testCase.scidAlias, localScid)
|
require.Equal(t, testCase.scidAlias, localScid)
|
||||||
require.Equal(t, testCase.expectsErr, err)
|
require.Equal(t, testCase.expectsErr, err)
|
||||||
|
|
||||||
_, rChan, rCommit, err := negotiateCommitmentType(
|
rChan, rCommit, err := negotiateCommitmentType(
|
||||||
channelType, remoteFeatures, localFeatures,
|
channelType, remoteFeatures, localFeatures,
|
||||||
testCase.mustBeExplicit,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if rChan != nil {
|
if rChan != nil {
|
||||||
@ -402,11 +383,11 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
require.Equal(
|
require.Equal(
|
||||||
t, testCase.expectsChanType, *lChan,
|
t, testCase.expectsChanType, lChan,
|
||||||
testCase.name,
|
testCase.name,
|
||||||
)
|
)
|
||||||
require.Equal(
|
require.Equal(
|
||||||
t, testCase.expectsChanType, *rChan,
|
t, testCase.expectsChanType, rChan,
|
||||||
testCase.name,
|
testCase.name,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -1504,9 +1504,8 @@ func (f *Manager) handleFundingOpen(peer lnpeer.Peer,
|
|||||||
// the remote peer are signaling the proper feature bit if we're using
|
// the remote peer are signaling the proper feature bit if we're using
|
||||||
// implicit negotiation, and simply the channel type sent over if we're
|
// implicit negotiation, and simply the channel type sent over if we're
|
||||||
// using explicit negotiation.
|
// using explicit negotiation.
|
||||||
wasExplicit, _, commitType, err := negotiateCommitmentType(
|
chanType, commitType, err := negotiateCommitmentType(
|
||||||
msg.ChannelType, peer.LocalFeatures(), peer.RemoteFeatures(),
|
msg.ChannelType, peer.LocalFeatures(), peer.RemoteFeatures(),
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(roasbeef): should be using soft errors
|
// TODO(roasbeef): should be using soft errors
|
||||||
@ -1525,19 +1524,16 @@ func (f *Manager) handleFundingOpen(peer lnpeer.Peer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
chanTypeFeatureBits *lnwire.ChannelType
|
zeroConf bool
|
||||||
zeroConf bool
|
scid bool
|
||||||
scid bool
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if wasExplicit {
|
// Only echo back a channel type in AcceptChannel if we actually used
|
||||||
// Only echo back a channel type in AcceptChannel if we actually
|
// explicit negotiation above.
|
||||||
// used explicit negotiation above.
|
if chanType != nil {
|
||||||
chanTypeFeatureBits = msg.ChannelType
|
|
||||||
|
|
||||||
// Check if the channel type includes the zero-conf or
|
// Check if the channel type includes the zero-conf or
|
||||||
// scid-alias bits.
|
// scid-alias bits.
|
||||||
featureVec := lnwire.RawFeatureVector(*chanTypeFeatureBits)
|
featureVec := lnwire.RawFeatureVector(*chanType)
|
||||||
zeroConf = featureVec.IsSet(lnwire.ZeroConfRequired)
|
zeroConf = featureVec.IsSet(lnwire.ZeroConfRequired)
|
||||||
scid = featureVec.IsSet(lnwire.ScidAliasRequired)
|
scid = featureVec.IsSet(lnwire.ScidAliasRequired)
|
||||||
|
|
||||||
@ -1773,7 +1769,7 @@ func (f *Manager) handleFundingOpen(peer lnpeer.Peer,
|
|||||||
remoteMaxHtlcs: maxHtlcs,
|
remoteMaxHtlcs: maxHtlcs,
|
||||||
remoteChanReserve: chanReserve,
|
remoteChanReserve: chanReserve,
|
||||||
maxLocalCsv: f.cfg.MaxLocalCSVDelay,
|
maxLocalCsv: f.cfg.MaxLocalCSVDelay,
|
||||||
channelType: msg.ChannelType,
|
channelType: chanType,
|
||||||
err: make(chan error, 1),
|
err: make(chan error, 1),
|
||||||
peer: peer,
|
peer: peer,
|
||||||
}
|
}
|
||||||
@ -1846,7 +1842,7 @@ func (f *Manager) handleFundingOpen(peer lnpeer.Peer,
|
|||||||
HtlcPoint: ourContribution.HtlcBasePoint.PubKey,
|
HtlcPoint: ourContribution.HtlcBasePoint.PubKey,
|
||||||
FirstCommitmentPoint: ourContribution.FirstCommitmentPoint,
|
FirstCommitmentPoint: ourContribution.FirstCommitmentPoint,
|
||||||
UpfrontShutdownScript: ourContribution.UpfrontShutdown,
|
UpfrontShutdownScript: ourContribution.UpfrontShutdown,
|
||||||
ChannelType: chanTypeFeatureBits,
|
ChannelType: chanType,
|
||||||
LeaseExpiry: msg.LeaseExpiry,
|
LeaseExpiry: msg.LeaseExpiry,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1926,18 +1922,16 @@ func (f *Manager) handleFundingAccept(peer lnpeer.Peer,
|
|||||||
} else if msg.ChannelType != nil {
|
} else if msg.ChannelType != nil {
|
||||||
// The spec isn't too clear about whether it's okay to set the
|
// The spec isn't too clear about whether it's okay to set the
|
||||||
// channel type in the accept_channel response if we didn't
|
// channel type in the accept_channel response if we didn't
|
||||||
// explicitly set it in the open_channel message. For now, let's
|
// explicitly set it in the open_channel message. For now, we
|
||||||
// just log the problem instead of failing the funding flow.
|
// check that it's the same type we'd have arrived through
|
||||||
_, implicitChannelType := implicitNegotiateCommitmentType(
|
// implicit negotiation. If it's another type, we fail the flow.
|
||||||
|
_, implicitCommitType := implicitNegotiateCommitmentType(
|
||||||
peer.LocalFeatures(), peer.RemoteFeatures(),
|
peer.LocalFeatures(), peer.RemoteFeatures(),
|
||||||
)
|
)
|
||||||
|
|
||||||
// We pass in false here as the funder since at this point, we
|
_, negotiatedCommitType, err := negotiateCommitmentType(
|
||||||
// didn't set a chan type ourselves, so falling back to
|
|
||||||
// implicit funding is acceptable.
|
|
||||||
_, _, negotiatedChannelType, err := negotiateCommitmentType(
|
|
||||||
msg.ChannelType, peer.LocalFeatures(),
|
msg.ChannelType, peer.LocalFeatures(),
|
||||||
peer.RemoteFeatures(), false,
|
peer.RemoteFeatures(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := errors.New("received unexpected channel type")
|
err := errors.New("received unexpected channel type")
|
||||||
@ -1945,11 +1939,7 @@ func (f *Manager) handleFundingAccept(peer lnpeer.Peer,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even though we don't expect a channel type to be set when we
|
if implicitCommitType != negotiatedCommitType {
|
||||||
// didn't send one in the first place, we check that it's the
|
|
||||||
// same type we'd have arrived through implicit negotiation. If
|
|
||||||
// it's another type, we fail the flow.
|
|
||||||
if implicitChannelType != negotiatedChannelType {
|
|
||||||
err := errors.New("negotiated unexpected channel type")
|
err := errors.New("negotiated unexpected channel type")
|
||||||
f.failFundingFlow(peer, cid, err)
|
f.failFundingFlow(peer, cid, err)
|
||||||
return
|
return
|
||||||
@ -4152,9 +4142,9 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) {
|
|||||||
// Before we init the channel, we'll also check to see what commitment
|
// 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
|
// format we can use with this peer. This is dependent on *both* us and
|
||||||
// the remote peer are signaling the proper feature bit.
|
// the remote peer are signaling the proper feature bit.
|
||||||
_, chanType, commitType, err := negotiateCommitmentType(
|
chanType, commitType, err := negotiateCommitmentType(
|
||||||
msg.ChannelType, msg.Peer.LocalFeatures(),
|
msg.ChannelType, msg.Peer.LocalFeatures(),
|
||||||
msg.Peer.RemoteFeatures(), true,
|
msg.Peer.RemoteFeatures(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("channel type negotiation failed: %v", err)
|
log.Errorf("channel type negotiation failed: %v", err)
|
||||||
@ -4167,20 +4157,23 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) {
|
|||||||
scid bool
|
scid bool
|
||||||
)
|
)
|
||||||
|
|
||||||
// Check if the returned chanType includes either the zero-conf or
|
if chanType != nil {
|
||||||
// scid-alias bits.
|
// Check if the returned chanType includes either the zero-conf
|
||||||
featureVec := lnwire.RawFeatureVector(*chanType)
|
// or scid-alias bits.
|
||||||
zeroConf = featureVec.IsSet(lnwire.ZeroConfRequired)
|
featureVec := lnwire.RawFeatureVector(*chanType)
|
||||||
scid = featureVec.IsSet(lnwire.ScidAliasRequired)
|
zeroConf = featureVec.IsSet(lnwire.ZeroConfRequired)
|
||||||
|
scid = featureVec.IsSet(lnwire.ScidAliasRequired)
|
||||||
|
|
||||||
// The option-scid-alias channel type for a public channel is
|
// The option-scid-alias channel type for a public channel is
|
||||||
// disallowed.
|
// disallowed.
|
||||||
if scid && !msg.Private {
|
if scid && !msg.Private {
|
||||||
err = fmt.Errorf("option-scid-alias chantype for public " +
|
err = fmt.Errorf("option-scid-alias chantype for " +
|
||||||
"channel")
|
"public channel")
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
msg.Err <- err
|
msg.Err <- err
|
||||||
return
|
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, we'll query the fee estimator for a fee that should get the
|
// First, we'll query the fee estimator for a fee that should get the
|
||||||
@ -4338,7 +4331,7 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) {
|
|||||||
remoteMaxHtlcs: maxHtlcs,
|
remoteMaxHtlcs: maxHtlcs,
|
||||||
remoteChanReserve: chanReserve,
|
remoteChanReserve: chanReserve,
|
||||||
maxLocalCsv: maxCSV,
|
maxLocalCsv: maxCSV,
|
||||||
channelType: msg.ChannelType,
|
channelType: chanType,
|
||||||
reservation: reservation,
|
reservation: reservation,
|
||||||
peer: msg.Peer,
|
peer: msg.Peer,
|
||||||
updates: msg.Updates,
|
updates: msg.Updates,
|
||||||
|
@ -4507,3 +4507,62 @@ func TestCommitmentTypeFundmaxSanityCheck(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestFundingManagerNoEchoChanType tests that the funding flow is aborted if
|
||||||
|
// the peer fails to echo back the channel type in AcceptChannel.
|
||||||
|
func TestFundingManagerNoEchoChanType(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
alice, bob := setupFundingManagers(t)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
tearDownFundingManagers(t, alice, bob)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Alice and Bob will have the same set of feature bits in our test.
|
||||||
|
featureBits := []lnwire.FeatureBit{
|
||||||
|
lnwire.ExplicitChannelTypeOptional,
|
||||||
|
lnwire.StaticRemoteKeyOptional,
|
||||||
|
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||||
|
}
|
||||||
|
alice.localFeatures = featureBits
|
||||||
|
alice.remoteFeatures = featureBits
|
||||||
|
bob.localFeatures = featureBits
|
||||||
|
bob.remoteFeatures = featureBits
|
||||||
|
|
||||||
|
expectedChanType := (*lnwire.ChannelType)(lnwire.NewRawFeatureVector(
|
||||||
|
lnwire.StaticRemoteKeyRequired,
|
||||||
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
|
))
|
||||||
|
|
||||||
|
// Create a funding request and start the workflow.
|
||||||
|
updateChan := make(chan *lnrpc.OpenStatusUpdate)
|
||||||
|
errChan := make(chan error, 1)
|
||||||
|
initReq := &InitFundingMsg{
|
||||||
|
Peer: bob,
|
||||||
|
TargetPubkey: bob.privKey.PubKey(),
|
||||||
|
ChainHash: *fundingNetParams.GenesisHash,
|
||||||
|
LocalFundingAmt: 500000,
|
||||||
|
Updates: updateChan,
|
||||||
|
Err: errChan,
|
||||||
|
}
|
||||||
|
|
||||||
|
alice.fundingMgr.InitFundingWorkflow(initReq)
|
||||||
|
|
||||||
|
// Alice should have sent the OpenChannel message to Bob.
|
||||||
|
openChanMsg := expectOpenChannelMsg(t, alice.msgChan)
|
||||||
|
|
||||||
|
require.Equal(t, expectedChanType, openChanMsg.ChannelType)
|
||||||
|
|
||||||
|
// Let Bob handle the OpenChannel message.
|
||||||
|
bob.fundingMgr.ProcessFundingMsg(openChanMsg, alice)
|
||||||
|
|
||||||
|
acceptChanMsg, _ := assertFundingMsgSent(t, bob.msgChan,
|
||||||
|
"AcceptChannel").(*lnwire.AcceptChannel)
|
||||||
|
|
||||||
|
require.Equal(t, expectedChanType, acceptChanMsg.ChannelType)
|
||||||
|
|
||||||
|
// Drop the channel type and ensure Alice responds with an error.
|
||||||
|
acceptChanMsg.ChannelType = nil
|
||||||
|
alice.fundingMgr.ProcessFundingMsg(acceptChanMsg, bob)
|
||||||
|
assertFundingMsgSent(t, alice.msgChan, "Error")
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user