mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-08-29 07:00:55 +02: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:
@@ -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.
|
||||
|
Reference in New Issue
Block a user