mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-03 17:26:57 +01:00
funding: fix channel type negotiation bug
The bug manifests when a nil ChannelType is passed to the funding manager in InitFundingMsg. A default value for ChannelType is selected and sent in the OpenChannel message. However, a nil ChannelType is stored in the reservation context. This causes our ChannelType checks in handleFundingAccept to be bypassed. Usually this makes us end up in the "peer unexpectedly sent explicit ChannelType" case, where we can still recover by reselecting a default ChannelType and verifying it matches the one the peer sent. But if the peer sends a nil ChannelType, we miss it. While fixing the bug, I also tried to simplify the negotiation logic, as the complexity is likely what hid the bug in the first place. Now negotiateCommitmentType only returns the ChannelType to be used in OpenChannel/AcceptChannel and the CommitmentType to pass to the wallet. It will even return a nil ChannelType when we're supposed to use implicit negotiation, so we don't need to manually set it to nil for OpenChannel and AcceptChannel.
This commit is contained in:
parent
0ae9c63d64
commit
a8a50f32f5
4 changed files with 130 additions and 105 deletions
|
@ -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.
|
||||||
|
@ -24,38 +18,47 @@ var (
|
||||||
// negotiateCommitmentType negotiates the commitment type of a newly opened
|
// negotiateCommitmentType negotiates the commitment type of a newly opened
|
||||||
// channel. If a desiredChanType 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
|
||||||
|
// is only returned if desiredChanType is not supported.
|
||||||
func negotiateCommitmentType(desiredChanType *lnwire.ChannelType, local,
|
func negotiateCommitmentType(desiredChanType *lnwire.ChannelType, local,
|
||||||
remote *lnwire.FeatureVector, mustBeExplicit bool) (bool,
|
remote *lnwire.FeatureVector) (*lnwire.ChannelType,
|
||||||
*lnwire.ChannelType, lnwallet.CommitmentType, error) {
|
lnwallet.CommitmentType, error) {
|
||||||
|
|
||||||
if desiredChanType != nil {
|
|
||||||
// If the peer does know explicit negotiation, let's attempt
|
|
||||||
// that now.
|
|
||||||
if hasFeatures(
|
|
||||||
local, remote, lnwire.ExplicitChannelTypeOptional,
|
|
||||||
) {
|
|
||||||
|
|
||||||
|
// BOLT#2 specifies we MUST use explicit negotiation if both peers
|
||||||
|
// signal for it.
|
||||||
|
if hasFeatures(local, remote, lnwire.ExplicitChannelTypeOptional) {
|
||||||
|
if desiredChanType != nil {
|
||||||
commitType, err := explicitNegotiateCommitmentType(
|
commitType, err := explicitNegotiateCommitmentType(
|
||||||
*desiredChanType, local, remote,
|
*desiredChanType, local, remote,
|
||||||
)
|
)
|
||||||
return true, desiredChanType, commitType, err
|
|
||||||
|
return desiredChanType, commitType, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're the funder, and we are attempting to use an
|
// Explicitly signal the "implicit" negotiation commitment type
|
||||||
// explicit channel type, but the remote party doesn't signal
|
// as default when a desired channel type isn't specified.
|
||||||
// the bit, then we actually want to exit here, to ensure the
|
chanType, commitType := implicitNegotiateCommitmentType(local,
|
||||||
// user doesn't end up with an unexpected channel type via
|
remote)
|
||||||
// implicit negotiation.
|
|
||||||
if mustBeExplicit {
|
return chanType, commitType, nil
|
||||||
return false, nil, 0, errUnsupportedExplicitNegotiation
|
}
|
||||||
|
|
||||||
|
// Otherwise, we'll use implicit negotiation. In this case, we are
|
||||||
|
// restricted to the newest channel type advertised. If the passed-in
|
||||||
|
// channelType doesn't match what was advertised, we fail.
|
||||||
|
chanType, commitType := implicitNegotiateCommitmentType(local, remote)
|
||||||
|
|
||||||
|
if desiredChanType != nil {
|
||||||
|
expected := lnwire.RawFeatureVector(*desiredChanType)
|
||||||
|
actual := lnwire.RawFeatureVector(*chanType)
|
||||||
|
if !expected.Equals(&actual) {
|
||||||
|
return nil, 0, errUnsupportedChannelType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chanType, commitType := implicitNegotiateCommitmentType(local, remote)
|
return nil, commitType, nil
|
||||||
return false, chanType, commitType, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// explicitNegotiateCommitmentType attempts to explicitly negotiate for a
|
// explicitNegotiateCommitmentType attempts to explicitly negotiate for a
|
||||||
|
|
|
@ -15,7 +15,6 @@ 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
|
||||||
|
@ -41,32 +40,8 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
||||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||||
),
|
),
|
||||||
expectsCommitType: lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx,
|
expectsCommitType: lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx,
|
||||||
expectsChanType: (*lnwire.ChannelType)(
|
expectsChanType: nil,
|
||||||
lnwire.NewRawFeatureVector(
|
expectsErr: nil,
|
||||||
lnwire.StaticRemoteKeyRequired,
|
|
||||||
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",
|
||||||
|
@ -282,9 +257,9 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
||||||
},
|
},
|
||||||
// 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,
|
||||||
|
@ -316,12 +291,8 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
||||||
lnwire.StaticRemoteKeyOptional,
|
lnwire.StaticRemoteKeyOptional,
|
||||||
),
|
),
|
||||||
expectsCommitType: lnwallet.CommitmentTypeTweakless,
|
expectsCommitType: lnwallet.CommitmentTypeTweakless,
|
||||||
expectsChanType: (*lnwire.ChannelType)(
|
expectsChanType: nil,
|
||||||
lnwire.NewRawFeatureVector(
|
expectsErr: nil,
|
||||||
lnwire.StaticRemoteKeyRequired,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
expectsErr: nil,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "implicit legacy",
|
name: "implicit legacy",
|
||||||
|
@ -332,10 +303,8 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
||||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||||
),
|
),
|
||||||
expectsCommitType: lnwallet.CommitmentTypeLegacy,
|
expectsCommitType: lnwallet.CommitmentTypeLegacy,
|
||||||
expectsChanType: (*lnwire.ChannelType)(
|
expectsChanType: nil,
|
||||||
lnwire.NewRawFeatureVector(),
|
expectsErr: nil,
|
||||||
),
|
|
||||||
expectsErr: nil,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,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 (
|
||||||
|
@ -383,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 {
|
||||||
|
|
|
@ -1452,9 +1452,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
|
||||||
|
@ -1473,19 +1472,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)
|
||||||
|
|
||||||
|
@ -1727,7 +1723,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,
|
||||||
}
|
}
|
||||||
|
@ -1800,7 +1796,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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1888,12 +1884,9 @@ func (f *Manager) handleFundingAccept(peer lnpeer.Peer,
|
||||||
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.
|
|
||||||
_, _, negotiatedCommitType, 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")
|
||||||
|
@ -4055,9 +4048,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)
|
||||||
|
@ -4070,20 +4063,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
|
||||||
|
@ -4239,7 +4235,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,
|
||||||
|
|
|
@ -4493,3 +4493,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…
Add table
Reference in a new issue