You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
loop/utils_test.go

415 lines
11 KiB
Go

package loop
import (
"context"
"encoding/hex"
"math/big"
"testing"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/lndclient"
mock_lnd "github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/zpay32"
"github.com/stretchr/testify/require"
)
var (
chanID0 = lnwire.NewShortChanIDFromInt(10)
chanID1 = lnwire.NewShortChanIDFromInt(11)
chanID2 = lnwire.NewShortChanIDFromInt(12)
chanID3 = lnwire.NewShortChanIDFromInt(13)
chanID4 = lnwire.NewShortChanIDFromInt(14)
chanID5 = lnwire.NewShortChanIDFromInt(15)
// To generate a nodeID we'll have to perform a few steps.
//
// Step 1: We generate the corresponding Y value to an
// arbitrary X value on the secp25k1 curve. This
// is done outside this function, and the output
// is converted to string with big.Int.Text(16)
// and converted to []bytes. btcec.decompressPoint
// is used here to generate this Y value
//
// Step 2: Construct a btcec.PublicKey object with the
// aforementioned values
//
// Step 3: Convert the pubkey to a Vertex by passing a
// compressed pubkey. This compression looses the
// Y value as it can be inferred.
//
// The Vertex object mainly contains the X value information,
// and has the underlying []bytes type. We generate the Y
// value information ourselves as that is returned in the
// hophints, and we must ensure it's accuracy
// Generate origin NodeID
originYBytes, _ = hex.DecodeString(
"bde70df51939b94c9c24979fa7dd04ebd9b" +
"3572da7802290438af2a681895441",
)
pubKeyOrigin = &btcec.PublicKey{
X: big.NewInt(0),
Y: new(big.Int).SetBytes(originYBytes),
Curve: btcec.S256(),
}
origin, _ = route.NewVertexFromBytes(pubKeyOrigin.SerializeCompressed())
// Generate peer1 NodeID
pubKey1YBytes, _ = hex.DecodeString(
"598ec453728e0ffe0ae2f5e174243cf58f2" +
"a3f2c83d2457b43036db568b11093",
)
pubKeyPeer1 = &btcec.PublicKey{
X: big.NewInt(4),
Y: new(big.Int).SetBytes(pubKey1YBytes),
Curve: btcec.S256(),
}
peer1, _ = route.NewVertexFromBytes(pubKeyPeer1.SerializeCompressed())
// Generate peer2 NodeID
pubKey2YBytes, _ = hex.DecodeString(
"bde70df51939b94c9c24979fa7dd04ebd" +
"9b3572da7802290438af2a681895441",
)
pubKeyPeer2 = &btcec.PublicKey{
X: big.NewInt(1),
Y: new(big.Int).SetBytes(pubKey2YBytes),
Curve: btcec.S256(),
}
peer2, _ = route.NewVertexFromBytes(pubKeyPeer2.SerializeCompressed())
capacity = btcutil.Amount(10000)
// Channel0 is a public active channel with peer1 with 10k remote
// capacity.
channel0 = lndclient.ChannelInfo{
Active: true,
Private: false,
ChannelID: chanID0.ToUint64(),
PubKeyBytes: peer1,
LocalBalance: 0,
RemoteBalance: capacity,
Capacity: capacity,
}
channelEdge0 = lndclient.ChannelEdge{
ChannelID: chanID0.ToUint64(),
ChannelPoint: "b121f1d368b8f60648970bc36b37e7b9700d" +
"ed098c60b027e42e9c648e297501:0",
Capacity: capacity,
Node1: peer1,
Node2: origin,
Node1Policy: &lndclient.RoutingPolicy{
FeeBaseMsat: 0,
FeeRateMilliMsat: 0,
TimeLockDelta: 140,
},
Node2Policy: &lndclient.RoutingPolicy{
FeeBaseMsat: 0,
FeeRateMilliMsat: 0,
TimeLockDelta: 140,
},
}
// Channel1 is a private active channel with peer1 with 10k remote
// capacity.
channel1 = lndclient.ChannelInfo{
Active: true,
Private: true,
ChannelID: chanID1.ToUint64(),
PubKeyBytes: peer1,
LocalBalance: 0,
RemoteBalance: capacity,
Capacity: capacity,
}
channelEdge1 = lndclient.ChannelEdge{
ChannelID: chanID1.ToUint64(),
ChannelPoint: "b121f1d368b8f60648970bc36b37e7b9700d" +
"ed098c60b027e42e9c648e297502:0",
Capacity: capacity,
Node1: peer1,
Node2: origin,
Node1Policy: &lndclient.RoutingPolicy{
FeeBaseMsat: 1,
FeeRateMilliMsat: 1,
TimeLockDelta: 141,
},
Node2Policy: &lndclient.RoutingPolicy{
FeeBaseMsat: 0,
FeeRateMilliMsat: 0,
TimeLockDelta: 144,
},
}
// Channel2 is a private active channel with peer2 with 10k remote
// capacity.
channel2 = lndclient.ChannelInfo{
Active: true,
Private: true,
ChannelID: chanID2.ToUint64(),
PubKeyBytes: peer2,
LocalBalance: 0,
RemoteBalance: capacity,
Capacity: capacity,
}
channelEdge2 = lndclient.ChannelEdge{
ChannelID: chanID2.ToUint64(),
ChannelPoint: "b121f1d368b8f60648970bc36b37e7b9700d" +
"ed098c60b027e42e9c648e297502:0",
Capacity: capacity,
Node1: origin,
Node2: peer2,
Node1Policy: &lndclient.RoutingPolicy{
FeeBaseMsat: 0,
FeeRateMilliMsat: 0,
TimeLockDelta: 144,
},
Node2Policy: &lndclient.RoutingPolicy{
FeeBaseMsat: 2,
FeeRateMilliMsat: 2,
TimeLockDelta: 142,
},
}
// Channel3 is a private inactive channel with peer2 with 0 remote
// capacity.
channel3 = lndclient.ChannelInfo{
Active: false,
Private: true,
ChannelID: chanID3.ToUint64(),
PubKeyBytes: peer2,
LocalBalance: capacity,
RemoteBalance: 0,
Capacity: capacity,
}
channelEdge3 = lndclient.ChannelEdge{
ChannelID: chanID3.ToUint64(),
ChannelPoint: "b121f1d368b8f60648970bc36b37e7b9700d" +
"ed098c60b027e42e9c648e297502:0",
Capacity: capacity,
Node1: peer2,
Node2: origin,
Node1Policy: &lndclient.RoutingPolicy{
FeeBaseMsat: 3,
FeeRateMilliMsat: 3,
TimeLockDelta: 143,
},
Node2Policy: &lndclient.RoutingPolicy{
FeeBaseMsat: 0,
FeeRateMilliMsat: 0,
TimeLockDelta: 144,
},
}
// Channel4 is a private active channel with peer2 with 5k remote
// capacity.
channel4 = lndclient.ChannelInfo{
Active: true,
Private: true,
ChannelID: chanID4.ToUint64(),
PubKeyBytes: peer2,
LocalBalance: capacity / 2,
RemoteBalance: capacity / 2,
Capacity: capacity,
}
channelEdge4 = lndclient.ChannelEdge{
ChannelID: chanID4.ToUint64(),
ChannelPoint: "6fe4408bba52c0a0ee15365e107105de" +
"fabfc70c497556af69351c4cfbc167b:0",
Capacity: capacity,
Node1: origin,
Node2: peer2,
Node1Policy: &lndclient.RoutingPolicy{
FeeBaseMsat: 0,
FeeRateMilliMsat: 0,
TimeLockDelta: 144,
},
Node2Policy: &lndclient.RoutingPolicy{
FeeBaseMsat: 4,
FeeRateMilliMsat: 4,
TimeLockDelta: 144,
},
}
// Channel5 is a public active channel with peer2 with 5k remote
// capacity. It is useful to make peer2 public (give the testcase).
channel5 = lndclient.ChannelInfo{
Active: true,
Private: false,
ChannelID: chanID5.ToUint64(),
PubKeyBytes: peer2,
LocalBalance: capacity / 2,
RemoteBalance: capacity / 2,
Capacity: capacity,
}
channelEdge5 = lndclient.ChannelEdge{
ChannelID: chanID5.ToUint64(),
ChannelPoint: "abcde52c0a0ee15365e107105de" +
"fabfc70c497556af69351c4cfbc167b:0",
Capacity: capacity,
Node1: origin,
Node2: peer2,
Node1Policy: &lndclient.RoutingPolicy{
FeeBaseMsat: 0,
FeeRateMilliMsat: 0,
TimeLockDelta: 144,
},
Node2Policy: &lndclient.RoutingPolicy{
FeeBaseMsat: 5,
FeeRateMilliMsat: 5,
TimeLockDelta: 145,
},
}
)
func TestSelectHopHints(t *testing.T) {
tests := []struct {
name string
channels []lndclient.ChannelInfo
channelEdges map[uint64]*lndclient.ChannelEdge
expectedHopHints [][]zpay32.HopHint
amtMSat btcutil.Amount
numMaxHophints int
includeNodes map[route.Vertex]struct{}
expectedError error
}{
// Chan2: private, active, remote balance OK
// Chan3: private, !active
// Chan4: private, active, small remote balance (second round).
// Chan5: public, active => makes peer2 public.
{
name: "2 out of 4 selected",
channels: []lndclient.ChannelInfo{
channel2,
channel3,
channel4,
channel5,
},
channelEdges: map[uint64]*lndclient.ChannelEdge{
channel2.ChannelID: &channelEdge2,
channel3.ChannelID: &channelEdge3,
channel4.ChannelID: &channelEdge4,
channel5.ChannelID: &channelEdge5,
},
expectedHopHints: [][]zpay32.HopHint{
{{
NodeID: pubKeyPeer2,
ChannelID: channel2.ChannelID,
FeeBaseMSat: 2,
FeeProportionalMillionths: 2,
CLTVExpiryDelta: 142,
}},
{{
NodeID: pubKeyPeer2,
ChannelID: channel4.ChannelID,
FeeBaseMSat: 4,
FeeProportionalMillionths: 4,
CLTVExpiryDelta: 144,
}},
},
amtMSat: capacity,
numMaxHophints: 20,
includeNodes: make(map[route.Vertex]struct{}),
expectedError: nil,
},
// A variation of the above test case but nodes are filtered so
// we only add channel 1 (as channel 0 is public making peer1
// public).
{
name: "1 out of 6 selected",
channels: []lndclient.ChannelInfo{
channel0,
channel1,
channel2,
channel3,
channel4,
channel5,
},
channelEdges: map[uint64]*lndclient.ChannelEdge{
channel0.ChannelID: &channelEdge0,
channel1.ChannelID: &channelEdge1,
channel2.ChannelID: &channelEdge2,
channel3.ChannelID: &channelEdge3,
channel4.ChannelID: &channelEdge4,
channel5.ChannelID: &channelEdge5,
},
expectedHopHints: [][]zpay32.HopHint{
{{
NodeID: pubKeyPeer1,
ChannelID: channel1.ChannelID,
FeeBaseMSat: 1,
FeeProportionalMillionths: 1,
CLTVExpiryDelta: 141,
}},
},
amtMSat: capacity,
numMaxHophints: 20,
includeNodes: map[route.Vertex]struct{}{
peer1: {},
},
expectedError: nil,
},
// Chan1: private, active, remote balance OK => node not public
{
name: "1 private chan",
channels: []lndclient.ChannelInfo{
channel1,
},
channelEdges: map[uint64]*lndclient.ChannelEdge{
channel1.ChannelID: &channelEdge1,
},
expectedHopHints: [][]zpay32.HopHint{},
amtMSat: capacity,
numMaxHophints: 20,
includeNodes: make(map[route.Vertex]struct{}),
expectedError: nil,
},
// Chan4: private, active, small remote balance (second round).
// Chan5: public, active => makes peer2 public.
{
name: "1 out of 2 selected",
channels: []lndclient.ChannelInfo{
channel4,
channel5,
},
channelEdges: map[uint64]*lndclient.ChannelEdge{
channel4.ChannelID: &channelEdge4,
channel5.ChannelID: &channelEdge5,
},
expectedHopHints: [][]zpay32.HopHint{
{{
NodeID: pubKeyPeer2,
ChannelID: channel4.ChannelID,
FeeBaseMSat: 4,
FeeProportionalMillionths: 4,
CLTVExpiryDelta: 144,
}},
},
amtMSat: capacity,
numMaxHophints: 20,
includeNodes: make(map[route.Vertex]struct{}),
expectedError: nil,
},
}
for _, test := range tests {
test := test
ctx := context.Background()
lnd := mock_lnd.NewMockLnd()
lnd.Channels = test.channels
lnd.ChannelEdges = test.channelEdges
t.Run(test.name, func(t *testing.T) {
hopHints, err := SelectHopHints(
ctx, &lnd.LndServices, test.amtMSat,
test.numMaxHophints, test.includeNodes,
)
require.Equal(t, test.expectedError, err)
require.Equal(t, test.expectedHopHints, hopHints)
})
}
}