mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +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:
parent
13c15e8038
commit
1aa9626606
@ -184,13 +184,15 @@ func TestMultipleAcceptClients(t *testing.T) {
|
||||
queries = map[*lnwire.OpenChannel]*ChannelAcceptResponse{
|
||||
chan1: NewChannelAcceptResponse(
|
||||
true, nil, testUpfront, 1, 2, 3, 4, 5, 6,
|
||||
false,
|
||||
),
|
||||
chan2: NewChannelAcceptResponse(
|
||||
false, errChannelRejected, nil, 0, 0, 0,
|
||||
0, 0, 0,
|
||||
0, 0, 0, false,
|
||||
),
|
||||
chan3: NewChannelAcceptResponse(
|
||||
false, customError, nil, 0, 0, 0, 0, 0, 0,
|
||||
false,
|
||||
),
|
||||
}
|
||||
|
||||
@ -245,7 +247,7 @@ func TestInvalidResponse(t *testing.T) {
|
||||
PendingChannelID: chan1,
|
||||
}: NewChannelAcceptResponse(
|
||||
false, errChannelRejected, nil, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0, false,
|
||||
),
|
||||
}
|
||||
|
||||
@ -288,7 +290,7 @@ func TestInvalidReserve(t *testing.T) {
|
||||
DustLimit: dustLimit,
|
||||
}: NewChannelAcceptResponse(
|
||||
false, errChannelRejected, nil, 0, 0,
|
||||
0, reserve, 0, 0,
|
||||
0, reserve, 0, 0, false,
|
||||
),
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ func (c *ChainedAcceptor) Accept(req *ChannelAcceptRequest) *ChannelAcceptRespon
|
||||
|
||||
return NewChannelAcceptResponse(
|
||||
false, errChannelRejected, nil, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0, false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,10 @@ type ChannelAcceptResponse struct {
|
||||
// MinAcceptDepth is the minimum depth that the initiator of the
|
||||
// channel should wait before considering the channel open.
|
||||
MinAcceptDepth uint16
|
||||
|
||||
// ZeroConf indicates that the fundee wishes to send min_depth = 0 and
|
||||
// request a zero-conf channel with the counter-party.
|
||||
ZeroConf bool
|
||||
}
|
||||
|
||||
// NewChannelAcceptResponse is a constructor for a channel accept response,
|
||||
@ -72,7 +76,7 @@ type ChannelAcceptResponse struct {
|
||||
func NewChannelAcceptResponse(accept bool, acceptErr error,
|
||||
upfrontShutdown lnwire.DeliveryAddress, csvDelay, htlcLimit,
|
||||
minDepth uint16, reserve btcutil.Amount, inFlight,
|
||||
minHtlcIn lnwire.MilliSatoshi) *ChannelAcceptResponse {
|
||||
minHtlcIn lnwire.MilliSatoshi, zeroConf bool) *ChannelAcceptResponse {
|
||||
|
||||
resp := &ChannelAcceptResponse{
|
||||
UpfrontShutdown: upfrontShutdown,
|
||||
@ -82,6 +86,7 @@ func NewChannelAcceptResponse(accept bool, acceptErr error,
|
||||
HtlcLimit: htlcLimit,
|
||||
MinHtlcIn: minHtlcIn,
|
||||
MinAcceptDepth: minDepth,
|
||||
ZeroConf: zeroConf,
|
||||
}
|
||||
|
||||
// If we want to accept the channel, we return a response with a nil
|
||||
|
@ -20,6 +20,10 @@ const (
|
||||
fieldUpfrontShutdown = "upfront shutdown"
|
||||
)
|
||||
|
||||
var (
|
||||
errZeroConf = fmt.Errorf("zero-conf set with non-zero min-depth")
|
||||
)
|
||||
|
||||
// fieldMismatchError returns a merge error for a named field when we get two
|
||||
// channel acceptor responses which have different values set.
|
||||
func fieldMismatchError(name string, current, newValue interface{}) error {
|
||||
@ -27,6 +31,13 @@ func fieldMismatchError(name string, current, newValue interface{}) error {
|
||||
name, current, newValue)
|
||||
}
|
||||
|
||||
// mergeBool merges two boolean values.
|
||||
func mergeBool(current, newValue bool) bool {
|
||||
// If either is true, return true. It is not possible to have different
|
||||
// "non-zero" values like the other cases.
|
||||
return current || newValue
|
||||
}
|
||||
|
||||
// mergeInt64 merges two int64 values, failing if they have different non-zero
|
||||
// values.
|
||||
func mergeInt64(name string, current, newValue int64) (int64, error) {
|
||||
@ -117,6 +128,13 @@ func mergeResponse(current,
|
||||
}
|
||||
current.MinAcceptDepth = uint16(minDepth)
|
||||
|
||||
current.ZeroConf = mergeBool(current.ZeroConf, newValue.ZeroConf)
|
||||
|
||||
// Assert that if zero-conf is set, min-depth is zero.
|
||||
if current.ZeroConf && current.MinAcceptDepth != 0 {
|
||||
return current, errZeroConf
|
||||
}
|
||||
|
||||
reserve, err := mergeInt64(
|
||||
fieldReserve, int64(current.Reserve), int64(newValue.Reserve),
|
||||
)
|
||||
|
@ -167,6 +167,18 @@ func TestMergeResponse(t *testing.T) {
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
// Test the case where one response has ZeroConf set
|
||||
// and another has a non-zero min depth set.
|
||||
name: "zero conf conflict",
|
||||
current: ChannelAcceptResponse{
|
||||
ZeroConf: true,
|
||||
},
|
||||
new: ChannelAcceptResponse{
|
||||
MinAcceptDepth: 5,
|
||||
},
|
||||
err: errZeroConf,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
@ -107,7 +107,7 @@ func (r *RPCAcceptor) Accept(req *ChannelAcceptRequest) *ChannelAcceptResponse {
|
||||
// Create a rejection response which we can use for the cases where we
|
||||
// reject the channel.
|
||||
rejectChannel := NewChannelAcceptResponse(
|
||||
false, errChannelRejected, nil, 0, 0, 0, 0, 0, 0,
|
||||
false, errChannelRejected, nil, 0, 0, 0, 0, 0, 0, false,
|
||||
)
|
||||
|
||||
// Send the request to the newRequests channel.
|
||||
@ -216,6 +216,7 @@ func (r *RPCAcceptor) receiveResponses(errChan chan error,
|
||||
MaxHtlcCount: resp.MaxHtlcCount,
|
||||
MinHtlcIn: resp.MinHtlcIn,
|
||||
MinAcceptDepth: resp.MinAcceptDepth,
|
||||
ZeroConf: resp.ZeroConf,
|
||||
}
|
||||
|
||||
// We have received a decision for one of our channel
|
||||
@ -348,6 +349,7 @@ func (r *RPCAcceptor) sendAcceptRequests(errChan chan error,
|
||||
btcutil.Amount(resp.ReserveSat),
|
||||
lnwire.MilliSatoshi(resp.InFlightMaxMsat),
|
||||
lnwire.MilliSatoshi(resp.MinHtlcIn),
|
||||
resp.ZeroConf,
|
||||
)
|
||||
|
||||
// Delete the channel from the acceptRequests map.
|
||||
|
@ -1692,8 +1692,9 @@ func (c *ChannelGraph) PruneTip() (*chainhash.Hash, uint32, error) {
|
||||
// database, then ErrEdgeNotFound will be returned. If strictZombiePruning is
|
||||
// true, then when we mark these edges as zombies, we'll set up the keys such
|
||||
// that we require the node that failed to send the fresh update to be the one
|
||||
// that resurrects the channel from its zombie state.
|
||||
func (c *ChannelGraph) DeleteChannelEdges(strictZombiePruning bool,
|
||||
// that resurrects the channel from its zombie state. The markZombie bool
|
||||
// denotes whether or not to mark the channel as a zombie.
|
||||
func (c *ChannelGraph) DeleteChannelEdges(strictZombiePruning, markZombie bool,
|
||||
chanIDs ...uint64) error {
|
||||
|
||||
// TODO(roasbeef): possibly delete from node bucket if node has no more
|
||||
@ -1730,7 +1731,7 @@ func (c *ChannelGraph) DeleteChannelEdges(strictZombiePruning bool,
|
||||
byteOrder.PutUint64(rawChanID[:], chanID)
|
||||
err := c.delChannelEdge(
|
||||
edges, edgeIndex, chanIndex, zombieIndex, nodes,
|
||||
rawChanID[:], true, strictZombiePruning,
|
||||
rawChanID[:], markZombie, strictZombiePruning,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -378,7 +378,7 @@ func TestEdgeInsertionDeletion(t *testing.T) {
|
||||
|
||||
// Next, attempt to delete the edge from the database, again this
|
||||
// should proceed without any issues.
|
||||
if err := graph.DeleteChannelEdges(false, chanID); err != nil {
|
||||
if err := graph.DeleteChannelEdges(false, true, chanID); err != nil {
|
||||
t.Fatalf("unable to delete edge: %v", err)
|
||||
}
|
||||
assertNoEdge(t, graph, chanID)
|
||||
@ -398,7 +398,7 @@ func TestEdgeInsertionDeletion(t *testing.T) {
|
||||
|
||||
// Finally, attempt to delete a (now) non-existent edge within the
|
||||
// database, this should result in an error.
|
||||
err = graph.DeleteChannelEdges(false, chanID)
|
||||
err = graph.DeleteChannelEdges(false, true, chanID)
|
||||
if err != ErrEdgeNotFound {
|
||||
t.Fatalf("deleting a non-existent edge should fail!")
|
||||
}
|
||||
@ -1993,7 +1993,7 @@ func TestFilterKnownChanIDs(t *testing.T) {
|
||||
if err := graph.AddChannelEdge(&channel); err != nil {
|
||||
t.Fatalf("unable to create channel edge: %v", err)
|
||||
}
|
||||
err := graph.DeleteChannelEdges(false, channel.ChannelID)
|
||||
err := graph.DeleteChannelEdges(false, true, channel.ChannelID)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to mark edge zombie: %v", err)
|
||||
}
|
||||
@ -2251,7 +2251,7 @@ func TestFetchChanInfos(t *testing.T) {
|
||||
if err := graph.AddChannelEdge(&zombieChan); err != nil {
|
||||
t.Fatalf("unable to create channel edge: %v", err)
|
||||
}
|
||||
err = graph.DeleteChannelEdges(false, zombieChan.ChannelID)
|
||||
err = graph.DeleteChannelEdges(false, true, zombieChan.ChannelID)
|
||||
require.NoError(t, err, "unable to delete and mark edge zombie")
|
||||
edgeQuery = append(edgeQuery, zombieChanID.ToUint64())
|
||||
|
||||
@ -2789,7 +2789,9 @@ func TestNodeIsPublic(t *testing.T) {
|
||||
// graph. This will make Alice be seen as a private node as it no longer
|
||||
// has any advertised edges.
|
||||
for _, graph := range graphs {
|
||||
err := graph.DeleteChannelEdges(false, aliceBobEdge.ChannelID)
|
||||
err := graph.DeleteChannelEdges(
|
||||
false, true, aliceBobEdge.ChannelID,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to remove edge: %v", err)
|
||||
}
|
||||
@ -2806,7 +2808,9 @@ func TestNodeIsPublic(t *testing.T) {
|
||||
// completely remove the edge as it is not possible for her to know of
|
||||
// it without it being advertised.
|
||||
for i, graph := range graphs {
|
||||
err := graph.DeleteChannelEdges(false, bobCarolEdge.ChannelID)
|
||||
err := graph.DeleteChannelEdges(
|
||||
false, true, bobCarolEdge.ChannelID,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to remove edge: %v", err)
|
||||
}
|
||||
@ -2900,7 +2904,9 @@ func TestDisabledChannelIDs(t *testing.T) {
|
||||
}
|
||||
|
||||
// Delete the channel edge and ensure it is removed from the disabled list.
|
||||
if err = graph.DeleteChannelEdges(false, edgeInfo.ChannelID); err != nil {
|
||||
if err = graph.DeleteChannelEdges(
|
||||
false, true, edgeInfo.ChannelID,
|
||||
); err != nil {
|
||||
t.Fatalf("unable to delete channel edge: %v", err)
|
||||
}
|
||||
disabledChanIds, err = graph.DisabledChannelIDs()
|
||||
@ -3111,7 +3117,7 @@ func TestGraphZombieIndex(t *testing.T) {
|
||||
|
||||
// If we delete the edge and mark it as a zombie, then we should expect
|
||||
// to see it within the index.
|
||||
err = graph.DeleteChannelEdges(false, edge.ChannelID)
|
||||
err = graph.DeleteChannelEdges(false, true, edge.ChannelID)
|
||||
require.NoError(t, err, "unable to mark edge as zombie")
|
||||
isZombie, pubKey1, pubKey2 := graph.IsZombieEdge(edge.ChannelID)
|
||||
if !isZombie {
|
||||
|
@ -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)
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -979,6 +979,13 @@ message ChannelAcceptResponse {
|
||||
The number of confirmations we require before we consider the channel open.
|
||||
*/
|
||||
uint32 min_accept_depth = 10;
|
||||
|
||||
/*
|
||||
Whether the responder wants this to be a zero-conf channel. This will fail
|
||||
if either side does not have the scid-alias feature bit set. The minimum
|
||||
depth field must be zero if this is true.
|
||||
*/
|
||||
bool zero_conf = 11;
|
||||
}
|
||||
|
||||
message ChannelPoint {
|
||||
|
@ -3409,6 +3409,10 @@
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "The number of confirmations we require before we consider the channel open."
|
||||
},
|
||||
"zero_conf": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the responder wants this to be a zero-conf channel. This will fail\nif either side does not have the scid-alias feature bit set. The minimum\ndepth field must be zero if this is true."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -975,7 +975,9 @@ func (r *ChannelRouter) pruneZombieChans() error {
|
||||
toPrune = append(toPrune, chanID)
|
||||
log.Tracef("Pruning zombie channel with ChannelID(%v)", chanID)
|
||||
}
|
||||
err = r.cfg.Graph.DeleteChannelEdges(r.cfg.StrictZombiePruning, toPrune...)
|
||||
err = r.cfg.Graph.DeleteChannelEdges(
|
||||
r.cfg.StrictZombiePruning, true, toPrune...,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to delete zombie channels: %v", err)
|
||||
}
|
||||
|
@ -2576,7 +2576,7 @@ func abandonChanFromGraph(chanGraph *channeldb.ChannelGraph,
|
||||
|
||||
// If the channel ID is still in the graph, then that means the channel
|
||||
// is still open, so we'll now move to purge it from the graph.
|
||||
return chanGraph.DeleteChannelEdges(false, chanID)
|
||||
return chanGraph.DeleteChannelEdges(false, true, chanID)
|
||||
}
|
||||
|
||||
// abandonChan removes a channel from the database, graph and contract court.
|
||||
|
51
server.go
51
server.go
@ -1173,6 +1173,46 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Wrap the DeleteChannelEdges method so that the funding manager can
|
||||
// use it without depending on several layers of indirection.
|
||||
deleteAliasEdge := func(scid lnwire.ShortChannelID) (
|
||||
*channeldb.ChannelEdgePolicy, error) {
|
||||
|
||||
info, e1, e2, err := s.graphDB.FetchChannelEdgesByID(
|
||||
scid.ToUint64(),
|
||||
)
|
||||
if err == channeldb.ErrEdgeNotFound {
|
||||
// This is unlikely but there is a slim chance of this
|
||||
// being hit if lnd was killed via SIGKILL and the
|
||||
// funding manager was stepping through the delete
|
||||
// alias edge logic.
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Grab our key to find our policy.
|
||||
var ourKey [33]byte
|
||||
copy(ourKey[:], nodeKeyDesc.PubKey.SerializeCompressed())
|
||||
|
||||
var ourPolicy *channeldb.ChannelEdgePolicy
|
||||
if info != nil && info.NodeKey1Bytes == ourKey {
|
||||
ourPolicy = e1
|
||||
} else {
|
||||
ourPolicy = e2
|
||||
}
|
||||
|
||||
if ourPolicy == nil {
|
||||
// Something is wrong, so return an error.
|
||||
return nil, fmt.Errorf("we don't have an edge")
|
||||
}
|
||||
|
||||
err = s.graphDB.DeleteChannelEdges(
|
||||
false, false, scid.ToUint64(),
|
||||
)
|
||||
return ourPolicy, err
|
||||
}
|
||||
|
||||
s.fundingMgr, err = funding.NewFundingManager(funding.Config{
|
||||
NoWumboChans: !cfg.ProtocolOptions.Wumbo(),
|
||||
IDKey: nodeKeyDesc.PubKey,
|
||||
@ -1191,15 +1231,16 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
||||
SendAnnouncement: s.authGossiper.ProcessLocalAnnouncement,
|
||||
NotifyWhenOnline: s.NotifyWhenOnline,
|
||||
TempChanIDSeed: chanIDSeed,
|
||||
FindChannel: func(chanID lnwire.ChannelID) (
|
||||
*channeldb.OpenChannel, error) {
|
||||
FindChannel: func(node *btcec.PublicKey,
|
||||
chanID lnwire.ChannelID) (*channeldb.OpenChannel,
|
||||
error) {
|
||||
|
||||
dbChannels, err := s.chanStateDB.FetchAllChannels()
|
||||
nodeChans, err := s.chanStateDB.FetchOpenChannels(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, channel := range dbChannels {
|
||||
for _, channel := range nodeChans {
|
||||
if chanID.IsChanPoint(&channel.FundingOutpoint) {
|
||||
return channel, nil
|
||||
}
|
||||
@ -1357,6 +1398,8 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
||||
RegisteredChains: cfg.registeredChains,
|
||||
MaxAnchorsCommitFeeRate: chainfee.SatPerKVByte(
|
||||
s.cfg.MaxCommitFeeRateAnchors * 1000).FeePerKWeight(),
|
||||
DeleteAliasEdge: deleteAliasEdge,
|
||||
AliasManager: s.aliasMgr,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
Loading…
Reference in New Issue
Block a user