lnd/chanacceptor/merge_test.go
eugene 1aa9626606
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.
2022-07-07 17:10:28 -04:00

201 lines
4.1 KiB
Go

package chanacceptor
import (
"testing"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/stretchr/testify/require"
)
// TestMergeResponse tests merging of channel acceptor responses.
func TestMergeResponse(t *testing.T) {
var (
addr1 = lnwire.DeliveryAddress{1}
addr2 = lnwire.DeliveryAddress{2}
populatedResp = ChannelAcceptResponse{
UpfrontShutdown: addr1,
CSVDelay: 2,
Reserve: 3,
InFlightTotal: 4,
HtlcLimit: 5,
MinHtlcIn: 6,
MinAcceptDepth: 7,
}
)
tests := []struct {
name string
current ChannelAcceptResponse
new ChannelAcceptResponse
merged ChannelAcceptResponse
err error
}{
{
name: "same response",
current: populatedResp,
new: populatedResp,
merged: populatedResp,
err: nil,
},
{
name: "different upfront",
current: ChannelAcceptResponse{
UpfrontShutdown: addr1,
},
new: ChannelAcceptResponse{
UpfrontShutdown: addr2,
},
err: fieldMismatchError(fieldUpfrontShutdown, addr1, addr2),
},
{
name: "different csv",
current: ChannelAcceptResponse{
CSVDelay: 1,
},
new: ChannelAcceptResponse{
CSVDelay: 2,
},
err: fieldMismatchError(fieldCSV, 1, 2),
},
{
name: "different reserve",
current: ChannelAcceptResponse{
Reserve: 1,
},
new: ChannelAcceptResponse{
Reserve: 2,
},
err: fieldMismatchError(fieldReserve, 1, 2),
},
{
name: "different in flight",
current: ChannelAcceptResponse{
InFlightTotal: 1,
},
new: ChannelAcceptResponse{
InFlightTotal: 2,
},
err: fieldMismatchError(
fieldInFlightTotal, lnwire.MilliSatoshi(1),
lnwire.MilliSatoshi(2),
),
},
{
name: "different htlc limit",
current: ChannelAcceptResponse{
HtlcLimit: 1,
},
new: ChannelAcceptResponse{
HtlcLimit: 2,
},
err: fieldMismatchError(fieldHtlcLimit, 1, 2),
},
{
name: "different min in",
current: ChannelAcceptResponse{
MinHtlcIn: 1,
},
new: ChannelAcceptResponse{
MinHtlcIn: 2,
},
err: fieldMismatchError(
fieldMinIn, lnwire.MilliSatoshi(1),
lnwire.MilliSatoshi(2),
),
},
{
name: "different depth",
current: ChannelAcceptResponse{
MinAcceptDepth: 1,
},
new: ChannelAcceptResponse{
MinAcceptDepth: 2,
},
err: fieldMismatchError(fieldMinDep, 1, 2),
},
{
name: "merge all values",
current: ChannelAcceptResponse{
UpfrontShutdown: lnwire.DeliveryAddress{1},
CSVDelay: 1,
Reserve: 0,
InFlightTotal: 3,
HtlcLimit: 0,
MinHtlcIn: 5,
MinAcceptDepth: 0,
},
new: ChannelAcceptResponse{
UpfrontShutdown: nil,
CSVDelay: 0,
Reserve: 2,
InFlightTotal: 0,
HtlcLimit: 4,
MinHtlcIn: 0,
MinAcceptDepth: 6,
},
merged: ChannelAcceptResponse{
UpfrontShutdown: lnwire.DeliveryAddress{1},
CSVDelay: 1,
Reserve: 2,
InFlightTotal: 3,
HtlcLimit: 4,
MinHtlcIn: 5,
MinAcceptDepth: 6,
},
err: nil,
},
{
// Test the case where fields have the same non-zero
// value, and the case where only response value is
// non-zero.
name: "empty and identical",
current: ChannelAcceptResponse{
CSVDelay: 1,
Reserve: 2,
InFlightTotal: 0,
},
new: ChannelAcceptResponse{
CSVDelay: 0,
Reserve: 2,
InFlightTotal: 3,
},
merged: ChannelAcceptResponse{
CSVDelay: 1,
Reserve: 2,
InFlightTotal: 3,
},
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 {
test := test
t.Run(test.name, func(t *testing.T) {
resp, err := mergeResponse(test.current, test.new)
require.Equal(t, test.err, err)
// If we expect an error, exit early rather than compare
// our result.
if test.err != nil {
return
}
require.Equal(t, test.merged, resp)
})
}
}