mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-11-18 10:06:51 +01:00
server+funding: allow scid-alias, zero-conf chantypes, scid-alias
feature-bit channels This allows opening zero-conf chan-type, scid-alias chan-type, and scid-alias feature-bit channels. scid-alias chan-type channels are required to be private. Two paths are available for opening a zero-conf channel: * explicit chan-type negotiation * LDK carve-out where chan-types are not used, LND is on the receiving end, and a ChannelAcceptor is used to enable zero-conf When a zero-conf channel is negotiated, the funding manager: * sends a FundingLocked with an alias * waits for a FundingLocked from the remote peer * calls addToRouterGraph to persist the channel using our alias in the graph. The peer's alias is used to send them a ChannelUpdate. * wait for six confirmations. If public, the alias edge in the graph is deleted and replaced (not atomically) with the confirmed edge. Our policy is also read-and-replaced, but the counterparty's policy won't exist until they send it to us. When a scid-alias-feature channel is negotiated, the funding manager: * sends a FundingLocked with an alias: * calls addToRouterGraph, sends ChannelUpdate with the confirmed SCID since it exists. * when six confirmations occurs, the edge is deleted and re-inserted since the peer may have sent us an alias ChannelUpdate that we are storing in the graph. Since it is possible for a user to toggle the scid-alias-feature-bit to on while channels exist in the funding manager, care has been taken to ensure that an alias is ALWAYS sent in the funding_locked message if this happens.
This commit is contained in:
@@ -24,10 +24,12 @@ var (
|
||||
// negotiateCommitmentType negotiates the commitment type of a newly opened
|
||||
// channel. If a channelType is provided, explicit negotiation for said type
|
||||
// will be attempted if the set of both local and remote features support it.
|
||||
// Otherwise, implicit negotiation will be attempted.
|
||||
func negotiateCommitmentType(channelType *lnwire.ChannelType,
|
||||
local, remote *lnwire.FeatureVector, mustBeExplicit bool,
|
||||
) (bool, *lnwire.ChannelType, lnwallet.CommitmentType, error) {
|
||||
// Otherwise, implicit negotiation will be attempted. Two booleans are
|
||||
// returned letting the caller know if the option-scid-alias or zero-conf
|
||||
// channel types were negotiated.
|
||||
func negotiateCommitmentType(channelType *lnwire.ChannelType, local,
|
||||
remote *lnwire.FeatureVector, mustBeExplicit bool) (bool,
|
||||
*lnwire.ChannelType, lnwallet.CommitmentType, error) {
|
||||
|
||||
if channelType != nil {
|
||||
// If the peer does know explicit negotiation, let's attempt
|
||||
@@ -57,12 +59,127 @@ func negotiateCommitmentType(channelType *lnwire.ChannelType,
|
||||
// specific channel type. Since the channel type is comprised of a set of even
|
||||
// feature bits, we also make sure each feature is supported by both peers. An
|
||||
// error is returned if either peer does not support said channel type.
|
||||
func explicitNegotiateCommitmentType(channelType lnwire.ChannelType,
|
||||
local, remote *lnwire.FeatureVector) (lnwallet.CommitmentType, error) {
|
||||
func explicitNegotiateCommitmentType(channelType lnwire.ChannelType, local,
|
||||
remote *lnwire.FeatureVector) (lnwallet.CommitmentType, error) {
|
||||
|
||||
channelFeatures := lnwire.RawFeatureVector(channelType)
|
||||
|
||||
switch {
|
||||
// Lease script enforcement + anchors zero fee + static remote key +
|
||||
// zero conf + scid alias features only.
|
||||
case channelFeatures.OnlyContains(
|
||||
lnwire.ZeroConfRequired,
|
||||
lnwire.ScidAliasRequired,
|
||||
lnwire.ScriptEnforcedLeaseRequired,
|
||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||
lnwire.StaticRemoteKeyRequired,
|
||||
):
|
||||
if !hasFeatures(
|
||||
local, remote,
|
||||
lnwire.ZeroConfOptional,
|
||||
lnwire.ScriptEnforcedLeaseOptional,
|
||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||
lnwire.StaticRemoteKeyOptional,
|
||||
) {
|
||||
|
||||
return 0, errUnsupportedChannelType
|
||||
}
|
||||
return lnwallet.CommitmentTypeScriptEnforcedLease, nil
|
||||
|
||||
// Anchors zero fee + static remote key + zero conf + scid alias
|
||||
// features only.
|
||||
case channelFeatures.OnlyContains(
|
||||
lnwire.ZeroConfRequired,
|
||||
lnwire.ScidAliasRequired,
|
||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||
lnwire.StaticRemoteKeyRequired,
|
||||
):
|
||||
if !hasFeatures(
|
||||
local, remote,
|
||||
lnwire.ZeroConfOptional,
|
||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||
lnwire.StaticRemoteKeyOptional,
|
||||
) {
|
||||
|
||||
return 0, errUnsupportedChannelType
|
||||
}
|
||||
return lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx, nil
|
||||
|
||||
// Lease script enforcement + anchors zero fee + static remote key +
|
||||
// zero conf features only.
|
||||
case channelFeatures.OnlyContains(
|
||||
lnwire.ZeroConfRequired,
|
||||
lnwire.ScriptEnforcedLeaseRequired,
|
||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||
lnwire.StaticRemoteKeyRequired,
|
||||
):
|
||||
if !hasFeatures(
|
||||
local, remote,
|
||||
lnwire.ZeroConfOptional,
|
||||
lnwire.ScriptEnforcedLeaseOptional,
|
||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||
lnwire.StaticRemoteKeyOptional,
|
||||
) {
|
||||
|
||||
return 0, errUnsupportedChannelType
|
||||
}
|
||||
return lnwallet.CommitmentTypeScriptEnforcedLease, nil
|
||||
|
||||
// Anchors zero fee + static remote key + zero conf features only.
|
||||
case channelFeatures.OnlyContains(
|
||||
lnwire.ZeroConfRequired,
|
||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||
lnwire.StaticRemoteKeyRequired,
|
||||
):
|
||||
if !hasFeatures(
|
||||
local, remote,
|
||||
lnwire.ZeroConfOptional,
|
||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||
lnwire.StaticRemoteKeyOptional,
|
||||
) {
|
||||
|
||||
return 0, errUnsupportedChannelType
|
||||
}
|
||||
return lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx, nil
|
||||
|
||||
// Lease script enforcement + anchors zero fee + static remote key +
|
||||
// option-scid-alias features only.
|
||||
case channelFeatures.OnlyContains(
|
||||
lnwire.ScidAliasRequired,
|
||||
lnwire.ScriptEnforcedLeaseRequired,
|
||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||
lnwire.StaticRemoteKeyRequired,
|
||||
):
|
||||
if !hasFeatures(
|
||||
local, remote,
|
||||
lnwire.ScidAliasOptional,
|
||||
lnwire.ScriptEnforcedLeaseOptional,
|
||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||
lnwire.StaticRemoteKeyOptional,
|
||||
) {
|
||||
|
||||
return 0, errUnsupportedChannelType
|
||||
}
|
||||
return lnwallet.CommitmentTypeScriptEnforcedLease, nil
|
||||
|
||||
// Anchors zero fee + static remote key + option-scid-alias features
|
||||
// only.
|
||||
case channelFeatures.OnlyContains(
|
||||
lnwire.ScidAliasRequired,
|
||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||
lnwire.StaticRemoteKeyRequired,
|
||||
):
|
||||
if !hasFeatures(
|
||||
local, remote,
|
||||
lnwire.ScidAliasOptional,
|
||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||
lnwire.StaticRemoteKeyOptional,
|
||||
) {
|
||||
|
||||
return 0, errUnsupportedChannelType
|
||||
}
|
||||
return lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx, nil
|
||||
|
||||
// Lease script enforcement + anchors zero fee + static remote key
|
||||
// features only.
|
||||
case channelFeatures.OnlyContains(
|
||||
|
||||
@@ -21,6 +21,8 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
||||
remoteFeatures *lnwire.RawFeatureVector
|
||||
expectsCommitType lnwallet.CommitmentType
|
||||
expectsChanType lnwire.ChannelType
|
||||
zeroConf bool
|
||||
scidAlias bool
|
||||
expectsErr error
|
||||
}{
|
||||
{
|
||||
@@ -81,6 +83,134 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
||||
),
|
||||
expectsErr: errUnsupportedChannelType,
|
||||
},
|
||||
{
|
||||
name: "explicit zero-conf script enforced",
|
||||
channelFeatures: lnwire.NewRawFeatureVector(
|
||||
lnwire.ZeroConfRequired,
|
||||
lnwire.StaticRemoteKeyRequired,
|
||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||
lnwire.ScriptEnforcedLeaseRequired,
|
||||
),
|
||||
localFeatures: lnwire.NewRawFeatureVector(
|
||||
lnwire.ZeroConfOptional,
|
||||
lnwire.StaticRemoteKeyOptional,
|
||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||
lnwire.ScriptEnforcedLeaseOptional,
|
||||
lnwire.ExplicitChannelTypeOptional,
|
||||
),
|
||||
remoteFeatures: lnwire.NewRawFeatureVector(
|
||||
lnwire.ZeroConfOptional,
|
||||
lnwire.StaticRemoteKeyOptional,
|
||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||
lnwire.ScriptEnforcedLeaseOptional,
|
||||
lnwire.ExplicitChannelTypeOptional,
|
||||
),
|
||||
expectsCommitType: lnwallet.CommitmentTypeScriptEnforcedLease,
|
||||
expectsChanType: lnwire.ChannelType(
|
||||
*lnwire.NewRawFeatureVector(
|
||||
lnwire.ZeroConfRequired,
|
||||
lnwire.StaticRemoteKeyRequired,
|
||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||
lnwire.ScriptEnforcedLeaseRequired,
|
||||
),
|
||||
),
|
||||
zeroConf: true,
|
||||
expectsErr: nil,
|
||||
},
|
||||
{
|
||||
name: "explicit zero-conf anchors",
|
||||
channelFeatures: lnwire.NewRawFeatureVector(
|
||||
lnwire.ZeroConfRequired,
|
||||
lnwire.StaticRemoteKeyRequired,
|
||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||
),
|
||||
localFeatures: lnwire.NewRawFeatureVector(
|
||||
lnwire.ZeroConfOptional,
|
||||
lnwire.StaticRemoteKeyOptional,
|
||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||
lnwire.ExplicitChannelTypeOptional,
|
||||
),
|
||||
remoteFeatures: lnwire.NewRawFeatureVector(
|
||||
lnwire.ZeroConfOptional,
|
||||
lnwire.StaticRemoteKeyOptional,
|
||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||
lnwire.ExplicitChannelTypeOptional,
|
||||
),
|
||||
expectsCommitType: lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx,
|
||||
expectsChanType: lnwire.ChannelType(
|
||||
*lnwire.NewRawFeatureVector(
|
||||
lnwire.ZeroConfRequired,
|
||||
lnwire.StaticRemoteKeyRequired,
|
||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||
),
|
||||
),
|
||||
zeroConf: true,
|
||||
expectsErr: nil,
|
||||
},
|
||||
{
|
||||
name: "explicit scid-alias script enforced",
|
||||
channelFeatures: lnwire.NewRawFeatureVector(
|
||||
lnwire.ScidAliasRequired,
|
||||
lnwire.StaticRemoteKeyRequired,
|
||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||
lnwire.ScriptEnforcedLeaseRequired,
|
||||
),
|
||||
localFeatures: lnwire.NewRawFeatureVector(
|
||||
lnwire.ScidAliasOptional,
|
||||
lnwire.StaticRemoteKeyOptional,
|
||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||
lnwire.ScriptEnforcedLeaseOptional,
|
||||
lnwire.ExplicitChannelTypeOptional,
|
||||
),
|
||||
remoteFeatures: lnwire.NewRawFeatureVector(
|
||||
lnwire.ScidAliasOptional,
|
||||
lnwire.StaticRemoteKeyOptional,
|
||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||
lnwire.ScriptEnforcedLeaseOptional,
|
||||
lnwire.ExplicitChannelTypeOptional,
|
||||
),
|
||||
expectsCommitType: lnwallet.CommitmentTypeScriptEnforcedLease,
|
||||
expectsChanType: lnwire.ChannelType(
|
||||
*lnwire.NewRawFeatureVector(
|
||||
lnwire.ScidAliasRequired,
|
||||
lnwire.StaticRemoteKeyRequired,
|
||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||
lnwire.ScriptEnforcedLeaseRequired,
|
||||
),
|
||||
),
|
||||
scidAlias: true,
|
||||
expectsErr: nil,
|
||||
},
|
||||
{
|
||||
name: "explicit scid-alias anchors",
|
||||
channelFeatures: lnwire.NewRawFeatureVector(
|
||||
lnwire.ScidAliasRequired,
|
||||
lnwire.StaticRemoteKeyRequired,
|
||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||
),
|
||||
localFeatures: lnwire.NewRawFeatureVector(
|
||||
lnwire.ScidAliasOptional,
|
||||
lnwire.StaticRemoteKeyOptional,
|
||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||
lnwire.ExplicitChannelTypeOptional,
|
||||
),
|
||||
remoteFeatures: lnwire.NewRawFeatureVector(
|
||||
lnwire.ScidAliasOptional,
|
||||
lnwire.StaticRemoteKeyOptional,
|
||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||
lnwire.ExplicitChannelTypeOptional,
|
||||
),
|
||||
expectsCommitType: lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx,
|
||||
expectsChanType: lnwire.ChannelType(
|
||||
*lnwire.NewRawFeatureVector(
|
||||
lnwire.ScidAliasRequired,
|
||||
lnwire.StaticRemoteKeyRequired,
|
||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||
),
|
||||
),
|
||||
scidAlias: true,
|
||||
expectsErr: nil,
|
||||
},
|
||||
{
|
||||
name: "explicit anchors",
|
||||
channelFeatures: lnwire.NewRawFeatureVector(
|
||||
@@ -212,16 +342,50 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
||||
*testCase.channelFeatures,
|
||||
)
|
||||
}
|
||||
_, localChanType, localCommitType, err := negotiateCommitmentType(
|
||||
|
||||
_, lChan, lCommit, err := negotiateCommitmentType(
|
||||
channelType, localFeatures, remoteFeatures,
|
||||
testCase.mustBeExplicit,
|
||||
)
|
||||
|
||||
var (
|
||||
localZc bool
|
||||
localScid bool
|
||||
remoteZc bool
|
||||
remoteScid bool
|
||||
)
|
||||
|
||||
if lChan != nil {
|
||||
localFv := lnwire.RawFeatureVector(*lChan)
|
||||
localZc = localFv.IsSet(
|
||||
lnwire.ZeroConfRequired,
|
||||
)
|
||||
localScid = localFv.IsSet(
|
||||
lnwire.ScidAliasRequired,
|
||||
)
|
||||
}
|
||||
|
||||
require.Equal(t, testCase.zeroConf, localZc)
|
||||
require.Equal(t, testCase.scidAlias, localScid)
|
||||
require.Equal(t, testCase.expectsErr, err)
|
||||
|
||||
_, remoteChanType, remoteCommitType, err := negotiateCommitmentType(
|
||||
_, rChan, rCommit, err := negotiateCommitmentType(
|
||||
channelType, remoteFeatures, localFeatures,
|
||||
testCase.mustBeExplicit,
|
||||
)
|
||||
|
||||
if rChan != nil {
|
||||
remoteFv := lnwire.RawFeatureVector(*rChan)
|
||||
remoteZc = remoteFv.IsSet(
|
||||
lnwire.ZeroConfRequired,
|
||||
)
|
||||
remoteScid = remoteFv.IsSet(
|
||||
lnwire.ScidAliasRequired,
|
||||
)
|
||||
}
|
||||
|
||||
require.Equal(t, testCase.zeroConf, remoteZc)
|
||||
require.Equal(t, testCase.scidAlias, remoteScid)
|
||||
require.Equal(t, testCase.expectsErr, err)
|
||||
|
||||
if testCase.expectsErr != nil {
|
||||
@@ -229,20 +393,20 @@ func TestCommitmentTypeNegotiation(t *testing.T) {
|
||||
}
|
||||
|
||||
require.Equal(
|
||||
t, testCase.expectsCommitType, localCommitType,
|
||||
t, testCase.expectsCommitType, lCommit,
|
||||
testCase.name,
|
||||
)
|
||||
require.Equal(
|
||||
t, testCase.expectsCommitType, remoteCommitType,
|
||||
t, testCase.expectsCommitType, rCommit,
|
||||
testCase.name,
|
||||
)
|
||||
|
||||
require.Equal(
|
||||
t, testCase.expectsChanType, *localChanType,
|
||||
t, testCase.expectsChanType, *lChan,
|
||||
testCase.name,
|
||||
)
|
||||
require.Equal(
|
||||
t, testCase.expectsChanType, *remoteChanType,
|
||||
t, testCase.expectsChanType, *rChan,
|
||||
testCase.name,
|
||||
)
|
||||
})
|
||||
|
||||
@@ -19,3 +19,31 @@ type Controller interface {
|
||||
// represents a pending channel in the Controller implementation.
|
||||
IsPendingChannel([32]byte, lnpeer.Peer) bool
|
||||
}
|
||||
|
||||
// aliasHandler is an interface that abstracts the managing of aliases.
|
||||
type aliasHandler interface {
|
||||
// RequestAlias lets the funding manager request a unique SCID alias to
|
||||
// use in the funding_locked message.
|
||||
RequestAlias() (lnwire.ShortChannelID, error)
|
||||
|
||||
// PutPeerAlias lets the funding manager store the received alias SCID
|
||||
// in the funding_locked message.
|
||||
PutPeerAlias(lnwire.ChannelID, lnwire.ShortChannelID) error
|
||||
|
||||
// GetPeerAlias lets the funding manager lookup the received alias SCID
|
||||
// from the funding_locked message. This is not the same as GetAliases
|
||||
// which retrieves OUR aliases for a given channel.
|
||||
GetPeerAlias(lnwire.ChannelID) (lnwire.ShortChannelID, error)
|
||||
|
||||
// AddLocalAlias persists an alias to an underlying alias store.
|
||||
AddLocalAlias(lnwire.ShortChannelID, lnwire.ShortChannelID, bool) error
|
||||
|
||||
// GetAliases returns the set of aliases given the main SCID of a
|
||||
// channel. This SCID will be an alias for zero-conf channels and will
|
||||
// be the confirmed SCID otherwise.
|
||||
GetAliases(lnwire.ShortChannelID) []lnwire.ShortChannelID
|
||||
|
||||
// DeleteSixConfs removes the passed SCID from one of the underlying
|
||||
// alias store's indices.
|
||||
DeleteSixConfs(lnwire.ShortChannelID) error
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -115,8 +115,48 @@ var (
|
||||
testKeyLoc = keychain.KeyLocator{Family: keychain.KeyFamilyNodeKey}
|
||||
|
||||
fundingNetParams = chainreg.BitcoinTestNetParams
|
||||
|
||||
alias = lnwire.ShortChannelID{
|
||||
BlockHeight: 16_000_000,
|
||||
TxIndex: 0,
|
||||
TxPosition: 0,
|
||||
}
|
||||
)
|
||||
|
||||
type mockAliasMgr struct{}
|
||||
|
||||
func (m *mockAliasMgr) RequestAlias() (lnwire.ShortChannelID, error) {
|
||||
return alias, nil
|
||||
}
|
||||
|
||||
func (m *mockAliasMgr) PutPeerAlias(lnwire.ChannelID,
|
||||
lnwire.ShortChannelID) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockAliasMgr) GetPeerAlias(lnwire.ChannelID) (lnwire.ShortChannelID,
|
||||
error) {
|
||||
|
||||
return alias, nil
|
||||
}
|
||||
|
||||
func (m *mockAliasMgr) AddLocalAlias(lnwire.ShortChannelID,
|
||||
lnwire.ShortChannelID, bool) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockAliasMgr) GetAliases(
|
||||
lnwire.ShortChannelID) []lnwire.ShortChannelID {
|
||||
|
||||
return []lnwire.ShortChannelID{alias}
|
||||
}
|
||||
|
||||
func (m *mockAliasMgr) DeleteSixConfs(lnwire.ShortChannelID) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type mockNotifier struct {
|
||||
oneConfChannel chan *chainntnfs.TxConfirmation
|
||||
sixConfChannel chan *chainntnfs.TxConfirmation
|
||||
@@ -199,6 +239,8 @@ type testNode struct {
|
||||
mockChanEvent *mockChanEvent
|
||||
testDir string
|
||||
shutdownChannel chan struct{}
|
||||
reportScidChan chan struct{}
|
||||
localFeatures []lnwire.FeatureBit
|
||||
remoteFeatures []lnwire.FeatureBit
|
||||
|
||||
remotePeer *testNode
|
||||
@@ -234,7 +276,9 @@ func (n *testNode) QuitSignal() <-chan struct{} {
|
||||
}
|
||||
|
||||
func (n *testNode) LocalFeatures() *lnwire.FeatureVector {
|
||||
return lnwire.NewFeatureVector(nil, nil)
|
||||
return lnwire.NewFeatureVector(
|
||||
lnwire.NewRawFeatureVector(n.localFeatures...), nil,
|
||||
)
|
||||
}
|
||||
|
||||
func (n *testNode) RemoteFeatures() *lnwire.FeatureVector {
|
||||
@@ -307,10 +351,13 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
|
||||
epochChan: make(chan *chainntnfs.BlockEpoch, 2),
|
||||
}
|
||||
|
||||
aliasMgr := &mockAliasMgr{}
|
||||
|
||||
sentMessages := make(chan lnwire.Message)
|
||||
sentAnnouncements := make(chan lnwire.Message)
|
||||
publTxChan := make(chan *wire.MsgTx, 1)
|
||||
shutdownChan := make(chan struct{})
|
||||
reportScidChan := make(chan struct{})
|
||||
|
||||
wc := &mock.WalletController{
|
||||
RootKey: alicePrivKey,
|
||||
@@ -382,14 +429,16 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
|
||||
return lnwire.NodeAnnouncement{}, nil
|
||||
},
|
||||
TempChanIDSeed: chanIDSeed,
|
||||
FindChannel: func(chanID lnwire.ChannelID) (
|
||||
*channeldb.OpenChannel, error) {
|
||||
dbChannels, err := cdb.FetchAllChannels()
|
||||
FindChannel: func(node *btcec.PublicKey,
|
||||
chanID lnwire.ChannelID) (*channeldb.OpenChannel,
|
||||
error) {
|
||||
|
||||
nodeChans, err := cdb.FetchOpenChannels(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, channel := range dbChannels {
|
||||
for _, channel := range nodeChans {
|
||||
if chanID.IsChanPoint(&channel.FundingOutpoint) {
|
||||
return channel, nil
|
||||
}
|
||||
@@ -432,6 +481,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
|
||||
return nil
|
||||
},
|
||||
ReportShortChanID: func(wire.OutPoint) error {
|
||||
reportScidChan <- struct{}{}
|
||||
return nil
|
||||
},
|
||||
PublishTransaction: func(txn *wire.MsgTx, _ string) error {
|
||||
@@ -450,6 +500,12 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
|
||||
OpenChannelPredicate: chainedAcceptor,
|
||||
NotifyPendingOpenChannelEvent: evt.NotifyPendingOpenChannelEvent,
|
||||
RegisteredChains: chainreg.NewChainRegistry(),
|
||||
DeleteAliasEdge: func(scid lnwire.ShortChannelID) (
|
||||
*channeldb.ChannelEdgePolicy, error) {
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
AliasManager: aliasMgr,
|
||||
}
|
||||
|
||||
for _, op := range options {
|
||||
@@ -473,6 +529,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
|
||||
mockChanEvent: evt,
|
||||
testDir: tempTestDir,
|
||||
shutdownChannel: shutdownChan,
|
||||
reportScidChan: reportScidChan,
|
||||
addr: addr,
|
||||
}
|
||||
|
||||
@@ -542,6 +599,7 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) {
|
||||
},
|
||||
DefaultMinHtlcIn: 5,
|
||||
RequiredRemoteMaxValue: oldCfg.RequiredRemoteMaxValue,
|
||||
ReportShortChanID: oldCfg.ReportShortChanID,
|
||||
PublishTransaction: func(txn *wire.MsgTx, _ string) error {
|
||||
publishChan <- txn
|
||||
return nil
|
||||
@@ -552,6 +610,8 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) {
|
||||
ZombieSweeperInterval: oldCfg.ZombieSweeperInterval,
|
||||
ReservationTimeout: oldCfg.ReservationTimeout,
|
||||
OpenChannelPredicate: chainedAcceptor,
|
||||
DeleteAliasEdge: oldCfg.DeleteAliasEdge,
|
||||
AliasManager: oldCfg.AliasManager,
|
||||
})
|
||||
require.NoError(t, err, "failed recreating aliceFundingManager")
|
||||
|
||||
@@ -639,7 +699,7 @@ func openChannel(t *testing.T, alice, bob *testNode, localFundingAmt,
|
||||
|
||||
publ := fundChannel(
|
||||
t, alice, bob, localFundingAmt, pushAmt, false, numConfs,
|
||||
updateChan, announceChan,
|
||||
updateChan, announceChan, nil,
|
||||
)
|
||||
fundingOutPoint := &wire.OutPoint{
|
||||
Hash: publ.TxHash(),
|
||||
@@ -652,7 +712,8 @@ func openChannel(t *testing.T, alice, bob *testNode, localFundingAmt,
|
||||
// transaction is confirmed on-chain. Returns the funding tx.
|
||||
func fundChannel(t *testing.T, alice, bob *testNode, localFundingAmt,
|
||||
pushAmt btcutil.Amount, subtractFees bool, numConfs uint32,
|
||||
updateChan chan *lnrpc.OpenStatusUpdate, announceChan bool) *wire.MsgTx {
|
||||
updateChan chan *lnrpc.OpenStatusUpdate, announceChan bool,
|
||||
chanType *lnwire.ChannelType) *wire.MsgTx {
|
||||
|
||||
// Create a funding request and start the workflow.
|
||||
errChan := make(chan error, 1)
|
||||
@@ -665,6 +726,7 @@ func fundChannel(t *testing.T, alice, bob *testNode, localFundingAmt,
|
||||
PushAmt: lnwire.NewMSatFromSatoshis(pushAmt),
|
||||
FundingFeePerKw: 1000,
|
||||
Private: !announceChan,
|
||||
ChannelType: chanType,
|
||||
Updates: updateChan,
|
||||
Err: errChan,
|
||||
}
|
||||
@@ -3231,7 +3293,7 @@ func TestFundingManagerFundAll(t *testing.T) {
|
||||
pushAmt := btcutil.Amount(0)
|
||||
fundingTx := fundChannel(
|
||||
t, alice, bob, test.spendAmt, pushAmt, true, 1,
|
||||
updateChan, true,
|
||||
updateChan, true, nil,
|
||||
)
|
||||
|
||||
// Check whether the expected change output is present.
|
||||
@@ -3662,3 +3724,128 @@ func testUpfrontFailure(t *testing.T, pkscript []byte, expectErr bool) {
|
||||
require.True(t, ok, "did not receive AcceptChannel")
|
||||
}
|
||||
}
|
||||
|
||||
// TestFundingManagerZeroConf tests that the fundingmanager properly handles
|
||||
// the whole flow for zero-conf channels.
|
||||
func TestFundingManagerZeroConf(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
alice, bob := setupFundingManagers(t)
|
||||
defer tearDownFundingManagers(t, alice, bob)
|
||||
|
||||
// Alice and Bob will have the same set of feature bits in our test.
|
||||
featureBits := []lnwire.FeatureBit{
|
||||
lnwire.ZeroConfOptional,
|
||||
lnwire.ScidAliasOptional,
|
||||
lnwire.ExplicitChannelTypeOptional,
|
||||
lnwire.StaticRemoteKeyOptional,
|
||||
lnwire.AnchorsZeroFeeHtlcTxOptional,
|
||||
}
|
||||
alice.localFeatures = featureBits
|
||||
alice.remoteFeatures = featureBits
|
||||
bob.localFeatures = featureBits
|
||||
bob.remoteFeatures = featureBits
|
||||
|
||||
fundingAmt := btcutil.Amount(500000)
|
||||
pushAmt := btcutil.Amount(0)
|
||||
updateChan := make(chan *lnrpc.OpenStatusUpdate)
|
||||
|
||||
// Construct the zero-conf ChannelType for use in open_channel.
|
||||
channelTypeBits := []lnwire.FeatureBit{
|
||||
lnwire.ZeroConfRequired,
|
||||
lnwire.StaticRemoteKeyRequired,
|
||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||
}
|
||||
channelType := lnwire.ChannelType(
|
||||
*lnwire.NewRawFeatureVector(channelTypeBits...),
|
||||
)
|
||||
|
||||
// Call fundChannel with the zero-conf ChannelType.
|
||||
fundingTx := fundChannel(
|
||||
t, alice, bob, fundingAmt, pushAmt, false, 1, updateChan, true,
|
||||
&channelType,
|
||||
)
|
||||
fundingOp := &wire.OutPoint{
|
||||
Hash: fundingTx.TxHash(),
|
||||
Index: 0,
|
||||
}
|
||||
|
||||
// Assert that Bob's funding_locked message has an AliasScid.
|
||||
bobFundingLocked := assertFundingMsgSent(
|
||||
t, bob.msgChan, "FundingLocked",
|
||||
).(*lnwire.FundingLocked)
|
||||
require.NotNil(t, bobFundingLocked.AliasScid)
|
||||
require.Equal(t, *bobFundingLocked.AliasScid, alias)
|
||||
|
||||
// Do the same for Alice as well.
|
||||
aliceFundingLocked := assertFundingMsgSent(
|
||||
t, alice.msgChan, "FundingLocked",
|
||||
).(*lnwire.FundingLocked)
|
||||
require.NotNil(t, aliceFundingLocked.AliasScid)
|
||||
require.Equal(t, *aliceFundingLocked.AliasScid, alias)
|
||||
|
||||
// Exchange the funding_locked messages.
|
||||
alice.fundingMgr.ProcessFundingMsg(bobFundingLocked, bob)
|
||||
bob.fundingMgr.ProcessFundingMsg(aliceFundingLocked, alice)
|
||||
|
||||
// We'll assert that they both create new links.
|
||||
assertHandleFundingLocked(t, alice, bob)
|
||||
|
||||
// We'll now assert that both sides send ChannelAnnouncement and
|
||||
// ChannelUpdate messages.
|
||||
assertChannelAnnouncements(t, alice, bob, fundingAmt, nil, nil)
|
||||
|
||||
// We'll now wait for the OpenStatusUpdate_ChanOpen update.
|
||||
waitForOpenUpdate(t, updateChan)
|
||||
|
||||
// Assert that both Alice & Bob are in the addedToRouterGraph state.
|
||||
assertAddedToRouterGraph(t, alice, bob, fundingOp)
|
||||
|
||||
// We'll now restart Alice's funding manager and assert that the tx
|
||||
// is rebroadcast.
|
||||
recreateAliceFundingManager(t, alice)
|
||||
|
||||
select {
|
||||
case <-alice.publTxChan:
|
||||
case <-time.After(time.Second * 5):
|
||||
t.Fatalf("timed out waiting for alice to rebroadcast tx")
|
||||
}
|
||||
|
||||
// We'll now confirm the funding transaction.
|
||||
alice.mockNotifier.sixConfChannel <- &chainntnfs.TxConfirmation{
|
||||
Tx: fundingTx,
|
||||
}
|
||||
bob.mockNotifier.sixConfChannel <- &chainntnfs.TxConfirmation{
|
||||
Tx: fundingTx,
|
||||
}
|
||||
|
||||
assertChannelAnnouncements(t, alice, bob, fundingAmt, nil, nil)
|
||||
|
||||
// Both Alice and Bob should send on reportScidChan.
|
||||
select {
|
||||
case <-alice.reportScidChan:
|
||||
case <-time.After(time.Second * 5):
|
||||
t.Fatalf("did not call ReportShortChanID in time")
|
||||
}
|
||||
|
||||
select {
|
||||
case <-bob.reportScidChan:
|
||||
case <-time.After(time.Second * 5):
|
||||
t.Fatalf("did not call ReportShortChanID in time")
|
||||
}
|
||||
|
||||
// Send along the 6-confirmation channel so that announcement sigs can
|
||||
// be exchanged.
|
||||
alice.mockNotifier.sixConfChannel <- &chainntnfs.TxConfirmation{
|
||||
Tx: fundingTx,
|
||||
}
|
||||
bob.mockNotifier.sixConfChannel <- &chainntnfs.TxConfirmation{
|
||||
Tx: fundingTx,
|
||||
}
|
||||
|
||||
assertAnnouncementSignatures(t, alice, bob)
|
||||
|
||||
// Assert that the channel state is deleted from the fundingmanager's
|
||||
// datastore.
|
||||
assertNoChannelState(t, alice, bob, fundingOp)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user