Merge pull request #1738 from joostjager/validation
payment failure message with attached channel_update not validated
This commit is contained in:
commit
25145acc46
@ -1520,7 +1520,7 @@ func (d *AuthenticatedGossiper) processRejectedEdge(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = ValidateChannelAnn(chanAnn)
|
err = routing.ValidateChannelAnn(chanAnn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("assembled channel announcement proof "+
|
err := fmt.Errorf("assembled channel announcement proof "+
|
||||||
"for shortChanID=%v isn't valid: %v",
|
"for shortChanID=%v isn't valid: %v",
|
||||||
@ -1598,7 +1598,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ValidateNodeAnn(msg); err != nil {
|
if err := routing.ValidateNodeAnn(msg); err != nil {
|
||||||
err := fmt.Errorf("unable to validate "+
|
err := fmt.Errorf("unable to validate "+
|
||||||
"node announcement: %v", err)
|
"node announcement: %v", err)
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
@ -1701,7 +1701,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(
|
|||||||
// formed.
|
// formed.
|
||||||
var proof *channeldb.ChannelAuthProof
|
var proof *channeldb.ChannelAuthProof
|
||||||
if nMsg.isRemote {
|
if nMsg.isRemote {
|
||||||
if err := ValidateChannelAnn(msg); err != nil {
|
if err := routing.ValidateChannelAnn(msg); err != nil {
|
||||||
err := fmt.Errorf("unable to validate "+
|
err := fmt.Errorf("unable to validate "+
|
||||||
"announcement: %v", err)
|
"announcement: %v", err)
|
||||||
d.rejectMtx.Lock()
|
d.rejectMtx.Lock()
|
||||||
@ -1993,7 +1993,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(
|
|||||||
// Validate the channel announcement with the expected public
|
// Validate the channel announcement with the expected public
|
||||||
// key, In the case of an invalid channel , we'll return an
|
// key, In the case of an invalid channel , we'll return an
|
||||||
// error to the caller and exit early.
|
// error to the caller and exit early.
|
||||||
if err := ValidateChannelUpdateAnn(pubKey, msg); err != nil {
|
if err := routing.ValidateChannelUpdateAnn(pubKey, msg); err != nil {
|
||||||
rErr := fmt.Errorf("unable to validate channel "+
|
rErr := fmt.Errorf("unable to validate channel "+
|
||||||
"update announcement for short_chan_id=%v: %v",
|
"update announcement for short_chan_id=%v: %v",
|
||||||
spew.Sdump(msg.ShortChannelID), err)
|
spew.Sdump(msg.ShortChannelID), err)
|
||||||
@ -2297,7 +2297,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(
|
|||||||
|
|
||||||
// With all the necessary components assembled validate the
|
// With all the necessary components assembled validate the
|
||||||
// full channel announcement proof.
|
// full channel announcement proof.
|
||||||
if err := ValidateChannelAnn(chanAnn); err != nil {
|
if err := routing.ValidateChannelAnn(chanAnn); err != nil {
|
||||||
err := fmt.Errorf("channel announcement proof "+
|
err := fmt.Errorf("channel announcement proof "+
|
||||||
"for short_chan_id=%v isn't valid: %v",
|
"for short_chan_id=%v isn't valid: %v",
|
||||||
shortChanID, err)
|
shortChanID, err)
|
||||||
@ -2505,7 +2505,7 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo,
|
|||||||
|
|
||||||
// To ensure that our signature is valid, we'll verify it ourself
|
// To ensure that our signature is valid, we'll verify it ourself
|
||||||
// before committing it to the slice returned.
|
// before committing it to the slice returned.
|
||||||
err = ValidateChannelUpdateAnn(d.selfKey, chanUpdate)
|
err = routing.ValidateChannelUpdateAnn(d.selfKey, chanUpdate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("generated invalid channel "+
|
return nil, nil, fmt.Errorf("generated invalid channel "+
|
||||||
"update sig: %v", err)
|
"update sig: %v", err)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package discovery
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
@ -35,6 +35,8 @@ var (
|
|||||||
0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53,
|
0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testTime = time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
|
||||||
|
|
||||||
priv1, _ = btcec.NewPrivateKey(btcec.S256())
|
priv1, _ = btcec.NewPrivateKey(btcec.S256())
|
||||||
bitcoinKey1 = priv1.PubKey()
|
bitcoinKey1 = priv1.PubKey()
|
||||||
|
|
||||||
@ -337,7 +339,7 @@ func (m *mockChainView) Stop() error {
|
|||||||
func TestEdgeUpdateNotification(t *testing.T) {
|
func TestEdgeUpdateNotification(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
ctx, cleanUp, err := createTestCtx(0)
|
ctx, cleanUp, err := createTestCtxSingleNode(0)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
@ -526,7 +528,7 @@ func TestNodeUpdateNotification(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight)
|
ctx, cleanUp, err := createTestCtxSingleNode(startingBlockHeight)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
@ -704,7 +706,7 @@ func TestNotificationCancellation(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight)
|
ctx, cleanUp, err := createTestCtxSingleNode(startingBlockHeight)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
@ -796,7 +798,7 @@ func TestChannelCloseNotification(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight)
|
ctx, cleanUp, err := createTestCtxSingleNode(startingBlockHeight)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
|
@ -14,7 +14,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/btcsuite/btcd/btcec"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
@ -22,8 +21,6 @@ import (
|
|||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
|
||||||
prand "math/rand"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -50,9 +47,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
randSource = prand.NewSource(time.Now().Unix())
|
testSig = &btcec.Signature{
|
||||||
randInts = prand.New(randSource)
|
|
||||||
testSig = &btcec.Signature{
|
|
||||||
R: new(big.Int),
|
R: new(big.Int),
|
||||||
S: new(big.Int),
|
S: new(big.Int),
|
||||||
}
|
}
|
||||||
@ -126,17 +121,12 @@ func makeTestGraph() (*channeldb.ChannelGraph, func(), error) {
|
|||||||
return cdb.ChannelGraph(), cleanUp, nil
|
return cdb.ChannelGraph(), cleanUp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// aliasMap is a map from a node's alias to its public key. This type is
|
|
||||||
// provided in order to allow easily look up from the human memorable alias
|
|
||||||
// to an exact node's public key.
|
|
||||||
type aliasMap map[string]*btcec.PublicKey
|
|
||||||
|
|
||||||
// parseTestGraph returns a fully populated ChannelGraph given a path to a JSON
|
// parseTestGraph returns a fully populated ChannelGraph given a path to a JSON
|
||||||
// file which encodes a test graph.
|
// file which encodes a test graph.
|
||||||
func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, error) {
|
func parseTestGraph(path string) (*testGraphInstance, error) {
|
||||||
graphJSON, err := ioutil.ReadFile(path)
|
graphJSON, err := ioutil.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// First unmarshal the JSON graph into an instance of the testGraph
|
// First unmarshal the JSON graph into an instance of the testGraph
|
||||||
@ -144,7 +134,7 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err
|
|||||||
// will be properly parsed into the struct above.
|
// will be properly parsed into the struct above.
|
||||||
var g testGraph
|
var g testGraph
|
||||||
if err := json.Unmarshal(graphJSON, &g); err != nil {
|
if err := json.Unmarshal(graphJSON, &g); err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll use this fake address for the IP address of all the nodes in
|
// We'll use this fake address for the IP address of all the nodes in
|
||||||
@ -153,14 +143,14 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err
|
|||||||
var testAddrs []net.Addr
|
var testAddrs []net.Addr
|
||||||
testAddr, err := net.ResolveTCPAddr("tcp", "192.0.0.1:8888")
|
testAddr, err := net.ResolveTCPAddr("tcp", "192.0.0.1:8888")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
testAddrs = append(testAddrs, testAddr)
|
testAddrs = append(testAddrs, testAddr)
|
||||||
|
|
||||||
// Next, create a temporary graph database for usage within the test.
|
// Next, create a temporary graph database for usage within the test.
|
||||||
graph, cleanUp, err := makeTestGraph()
|
graph, cleanUp, err := makeTestGraph()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
aliasMap := make(map[string]*btcec.PublicKey)
|
aliasMap := make(map[string]*btcec.PublicKey)
|
||||||
@ -170,13 +160,13 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err
|
|||||||
for _, node := range g.Nodes {
|
for _, node := range g.Nodes {
|
||||||
pubBytes, err := hex.DecodeString(node.PubKey)
|
pubBytes, err := hex.DecodeString(node.PubKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dbNode := &channeldb.LightningNode{
|
dbNode := &channeldb.LightningNode{
|
||||||
HaveNodeAnnouncement: true,
|
HaveNodeAnnouncement: true,
|
||||||
AuthSigBytes: testSig.Serialize(),
|
AuthSigBytes: testSig.Serialize(),
|
||||||
LastUpdate: time.Now(),
|
LastUpdate: testTime,
|
||||||
Addresses: testAddrs,
|
Addresses: testAddrs,
|
||||||
Alias: node.Alias,
|
Alias: node.Alias,
|
||||||
Features: testFeatures,
|
Features: testFeatures,
|
||||||
@ -186,13 +176,13 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err
|
|||||||
// We require all aliases within the graph to be unique for our
|
// We require all aliases within the graph to be unique for our
|
||||||
// tests.
|
// tests.
|
||||||
if _, ok := aliasMap[node.Alias]; ok {
|
if _, ok := aliasMap[node.Alias]; ok {
|
||||||
return nil, nil, nil, errors.New("aliases for nodes " +
|
return nil, errors.New("aliases for nodes " +
|
||||||
"must be unique!")
|
"must be unique!")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub, err := btcec.ParsePubKey(pubBytes, btcec.S256())
|
pub, err := btcec.ParsePubKey(pubBytes, btcec.S256())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the alias is unique, then add the node to the
|
// If the alias is unique, then add the node to the
|
||||||
@ -208,7 +198,7 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err
|
|||||||
// iteration, then the JSON has an error as only ONE
|
// iteration, then the JSON has an error as only ONE
|
||||||
// node can be the source in the graph.
|
// node can be the source in the graph.
|
||||||
if source != nil {
|
if source != nil {
|
||||||
return nil, nil, nil, errors.New("JSON is invalid " +
|
return nil, errors.New("JSON is invalid " +
|
||||||
"multiple nodes are tagged as the source")
|
"multiple nodes are tagged as the source")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,14 +208,14 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err
|
|||||||
// With the node fully parsed, add it as a vertex within the
|
// With the node fully parsed, add it as a vertex within the
|
||||||
// graph.
|
// graph.
|
||||||
if err := graph.AddLightningNode(dbNode); err != nil {
|
if err := graph.AddLightningNode(dbNode); err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if source != nil {
|
if source != nil {
|
||||||
// Set the selected source node
|
// Set the selected source node
|
||||||
if err := graph.SetSourceNode(source); err != nil {
|
if err := graph.SetSourceNode(source); err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,18 +224,18 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err
|
|||||||
for _, edge := range g.Edges {
|
for _, edge := range g.Edges {
|
||||||
node1Bytes, err := hex.DecodeString(edge.Node1)
|
node1Bytes, err := hex.DecodeString(edge.Node1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
node2Bytes, err := hex.DecodeString(edge.Node2)
|
node2Bytes, err := hex.DecodeString(edge.Node2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
fundingTXID := strings.Split(edge.ChannelPoint, ":")[0]
|
fundingTXID := strings.Split(edge.ChannelPoint, ":")[0]
|
||||||
txidBytes, err := chainhash.NewHashFromStr(fundingTXID)
|
txidBytes, err := chainhash.NewHashFromStr(fundingTXID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
fundingPoint := wire.OutPoint{
|
fundingPoint := wire.OutPoint{
|
||||||
Hash: *txidBytes,
|
Hash: *txidBytes,
|
||||||
@ -268,25 +258,29 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err
|
|||||||
|
|
||||||
err = graph.AddChannelEdge(&edgeInfo)
|
err = graph.AddChannelEdge(&edgeInfo)
|
||||||
if err != nil && err != channeldb.ErrEdgeAlreadyExist {
|
if err != nil && err != channeldb.ErrEdgeAlreadyExist {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
edgePolicy := &channeldb.ChannelEdgePolicy{
|
edgePolicy := &channeldb.ChannelEdgePolicy{
|
||||||
SigBytes: testSig.Serialize(),
|
SigBytes: testSig.Serialize(),
|
||||||
Flags: lnwire.ChanUpdateFlag(edge.Flags),
|
Flags: lnwire.ChanUpdateFlag(edge.Flags),
|
||||||
ChannelID: edge.ChannelID,
|
ChannelID: edge.ChannelID,
|
||||||
LastUpdate: time.Now(),
|
LastUpdate: testTime,
|
||||||
TimeLockDelta: edge.Expiry,
|
TimeLockDelta: edge.Expiry,
|
||||||
MinHTLC: lnwire.MilliSatoshi(edge.MinHTLC),
|
MinHTLC: lnwire.MilliSatoshi(edge.MinHTLC),
|
||||||
FeeBaseMSat: lnwire.MilliSatoshi(edge.FeeBaseMsat),
|
FeeBaseMSat: lnwire.MilliSatoshi(edge.FeeBaseMsat),
|
||||||
FeeProportionalMillionths: lnwire.MilliSatoshi(edge.FeeRate),
|
FeeProportionalMillionths: lnwire.MilliSatoshi(edge.FeeRate),
|
||||||
}
|
}
|
||||||
if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
|
if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return graph, cleanUp, aliasMap, nil
|
return &testGraphInstance{
|
||||||
|
graph: graph,
|
||||||
|
cleanUp: cleanUp,
|
||||||
|
aliasMap: aliasMap,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type testChannelPolicy struct {
|
type testChannelPolicy struct {
|
||||||
@ -314,7 +308,15 @@ func defaultTestChannelEnd(alias string) *testChannelEnd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func symmetricTestChannel(alias1 string, alias2 string, capacity btcutil.Amount,
|
func symmetricTestChannel(alias1 string, alias2 string, capacity btcutil.Amount,
|
||||||
policy *testChannelPolicy) *testChannel {
|
policy *testChannelPolicy, chanID ...uint64) *testChannel {
|
||||||
|
|
||||||
|
// Leaving id zero will result in auto-generation of a channel id during
|
||||||
|
// graph construction.
|
||||||
|
var id uint64
|
||||||
|
if len(chanID) > 0 {
|
||||||
|
id = chanID[0]
|
||||||
|
}
|
||||||
|
|
||||||
return &testChannel{
|
return &testChannel{
|
||||||
Capacity: capacity,
|
Capacity: capacity,
|
||||||
Node1: &testChannelEnd{
|
Node1: &testChannelEnd{
|
||||||
@ -325,38 +327,55 @@ func symmetricTestChannel(alias1 string, alias2 string, capacity btcutil.Amount,
|
|||||||
Alias: alias2,
|
Alias: alias2,
|
||||||
testChannelPolicy: *policy,
|
testChannelPolicy: *policy,
|
||||||
},
|
},
|
||||||
|
ChannelID: id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type testChannel struct {
|
type testChannel struct {
|
||||||
Node1 *testChannelEnd
|
Node1 *testChannelEnd
|
||||||
Node2 *testChannelEnd
|
Node2 *testChannelEnd
|
||||||
Capacity btcutil.Amount
|
Capacity btcutil.Amount
|
||||||
|
ChannelID uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// createTestGraph returns a fully populated ChannelGraph based on a set of
|
type testGraphInstance struct {
|
||||||
|
graph *channeldb.ChannelGraph
|
||||||
|
cleanUp func()
|
||||||
|
|
||||||
|
// aliasMap is a map from a node's alias to its public key. This type is
|
||||||
|
// provided in order to allow easily look up from the human memorable alias
|
||||||
|
// to an exact node's public key.
|
||||||
|
aliasMap map[string]*btcec.PublicKey
|
||||||
|
|
||||||
|
// privKeyMap maps a node alias to its private key. This is used to be
|
||||||
|
// able to mock a remote node's signing behaviour.
|
||||||
|
privKeyMap map[string]*btcec.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// createTestGraphFromChannels returns a fully populated ChannelGraph based on a set of
|
||||||
// test channels. Additional required information like keys are derived in
|
// test channels. Additional required information like keys are derived in
|
||||||
// a deterministical way and added to the channel graph. A list of nodes is
|
// a deterministical way and added to the channel graph. A list of nodes is
|
||||||
// not required and derived from the channel data. The goal is to keep
|
// not required and derived from the channel data. The goal is to keep
|
||||||
// instantiating a test channel graph as light weight as possible.
|
// instantiating a test channel graph as light weight as possible.
|
||||||
func createTestGraph(testChannels []*testChannel) (*channeldb.ChannelGraph, func(), aliasMap, error) {
|
func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstance, error) {
|
||||||
// We'll use this fake address for the IP address of all the nodes in
|
// We'll use this fake address for the IP address of all the nodes in
|
||||||
// our tests. This value isn't needed for path finding so it doesn't
|
// our tests. This value isn't needed for path finding so it doesn't
|
||||||
// need to be unique.
|
// need to be unique.
|
||||||
var testAddrs []net.Addr
|
var testAddrs []net.Addr
|
||||||
testAddr, err := net.ResolveTCPAddr("tcp", "192.0.0.1:8888")
|
testAddr, err := net.ResolveTCPAddr("tcp", "192.0.0.1:8888")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
testAddrs = append(testAddrs, testAddr)
|
testAddrs = append(testAddrs, testAddr)
|
||||||
|
|
||||||
// Next, create a temporary graph database for usage within the test.
|
// Next, create a temporary graph database for usage within the test.
|
||||||
graph, cleanUp, err := makeTestGraph()
|
graph, cleanUp, err := makeTestGraph()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
aliasMap := make(map[string]*btcec.PublicKey)
|
aliasMap := make(map[string]*btcec.PublicKey)
|
||||||
|
privKeyMap := make(map[string]*btcec.PrivateKey)
|
||||||
|
|
||||||
nodeIndex := byte(0)
|
nodeIndex := byte(0)
|
||||||
addNodeWithAlias := func(alias string) (*channeldb.LightningNode, error) {
|
addNodeWithAlias := func(alias string) (*channeldb.LightningNode, error) {
|
||||||
@ -368,13 +387,13 @@ func createTestGraph(testChannels []*testChannel) (*channeldb.ChannelGraph, func
|
|||||||
0, 0, 0, 0, 0, 0, 0, nodeIndex + 1,
|
0, 0, 0, 0, 0, 0, 0, nodeIndex + 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, pubKey := btcec.PrivKeyFromBytes(btcec.S256(),
|
privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(),
|
||||||
keyBytes)
|
keyBytes)
|
||||||
|
|
||||||
dbNode := &channeldb.LightningNode{
|
dbNode := &channeldb.LightningNode{
|
||||||
HaveNodeAnnouncement: true,
|
HaveNodeAnnouncement: true,
|
||||||
AuthSigBytes: testSig.Serialize(),
|
AuthSigBytes: testSig.Serialize(),
|
||||||
LastUpdate: time.Now(),
|
LastUpdate: testTime,
|
||||||
Addresses: testAddrs,
|
Addresses: testAddrs,
|
||||||
Alias: alias,
|
Alias: alias,
|
||||||
Features: testFeatures,
|
Features: testFeatures,
|
||||||
@ -382,6 +401,8 @@ func createTestGraph(testChannels []*testChannel) (*channeldb.ChannelGraph, func
|
|||||||
|
|
||||||
copy(dbNode.PubKeyBytes[:], pubKey.SerializeCompressed())
|
copy(dbNode.PubKeyBytes[:], pubKey.SerializeCompressed())
|
||||||
|
|
||||||
|
privKeyMap[alias] = privKey
|
||||||
|
|
||||||
// With the node fully parsed, add it as a vertex within the
|
// With the node fully parsed, add it as a vertex within the
|
||||||
// graph.
|
// graph.
|
||||||
if err := graph.AddLightningNode(dbNode); err != nil {
|
if err := graph.AddLightningNode(dbNode); err != nil {
|
||||||
@ -396,15 +417,18 @@ func createTestGraph(testChannels []*testChannel) (*channeldb.ChannelGraph, func
|
|||||||
|
|
||||||
var source *channeldb.LightningNode
|
var source *channeldb.LightningNode
|
||||||
if source, err = addNodeWithAlias("roasbeef"); err != nil {
|
if source, err = addNodeWithAlias("roasbeef"); err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the source node
|
// Set the source node
|
||||||
if err := graph.SetSourceNode(source); err != nil {
|
if err := graph.SetSourceNode(source); err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
channelID := uint64(0)
|
// Initialize variable that keeps track of the next channel id to assign
|
||||||
|
// if none is specified.
|
||||||
|
nextUnassignedChannelID := uint64(100000)
|
||||||
|
|
||||||
for _, testChannel := range testChannels {
|
for _, testChannel := range testChannels {
|
||||||
for _, alias := range []string{
|
for _, alias := range []string{
|
||||||
testChannel.Node1.Alias, testChannel.Node2.Alias} {
|
testChannel.Node1.Alias, testChannel.Node2.Alias} {
|
||||||
@ -415,6 +439,14 @@ func createTestGraph(testChannels []*testChannel) (*channeldb.ChannelGraph, func
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
channelID := testChannel.ChannelID
|
||||||
|
|
||||||
|
// If no channel id is specified, generate an id.
|
||||||
|
if channelID == 0 {
|
||||||
|
channelID = nextUnassignedChannelID
|
||||||
|
nextUnassignedChannelID++
|
||||||
|
}
|
||||||
|
|
||||||
var hash [sha256.Size]byte
|
var hash [sha256.Size]byte
|
||||||
hash[len(hash)-1] = byte(channelID)
|
hash[len(hash)-1] = byte(channelID)
|
||||||
|
|
||||||
@ -442,28 +474,28 @@ func createTestGraph(testChannels []*testChannel) (*channeldb.ChannelGraph, func
|
|||||||
|
|
||||||
err = graph.AddChannelEdge(&edgeInfo)
|
err = graph.AddChannelEdge(&edgeInfo)
|
||||||
if err != nil && err != channeldb.ErrEdgeAlreadyExist {
|
if err != nil && err != channeldb.ErrEdgeAlreadyExist {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
edgePolicy := &channeldb.ChannelEdgePolicy{
|
edgePolicy := &channeldb.ChannelEdgePolicy{
|
||||||
SigBytes: testSig.Serialize(),
|
SigBytes: testSig.Serialize(),
|
||||||
Flags: lnwire.ChanUpdateFlag(0),
|
Flags: lnwire.ChanUpdateFlag(0),
|
||||||
ChannelID: channelID,
|
ChannelID: channelID,
|
||||||
LastUpdate: time.Now(),
|
LastUpdate: testTime,
|
||||||
TimeLockDelta: testChannel.Node1.Expiry,
|
TimeLockDelta: testChannel.Node1.Expiry,
|
||||||
MinHTLC: testChannel.Node1.MinHTLC,
|
MinHTLC: testChannel.Node1.MinHTLC,
|
||||||
FeeBaseMSat: testChannel.Node1.FeeBaseMsat,
|
FeeBaseMSat: testChannel.Node1.FeeBaseMsat,
|
||||||
FeeProportionalMillionths: testChannel.Node1.FeeRate,
|
FeeProportionalMillionths: testChannel.Node1.FeeRate,
|
||||||
}
|
}
|
||||||
if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
|
if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
edgePolicy = &channeldb.ChannelEdgePolicy{
|
edgePolicy = &channeldb.ChannelEdgePolicy{
|
||||||
SigBytes: testSig.Serialize(),
|
SigBytes: testSig.Serialize(),
|
||||||
Flags: lnwire.ChanUpdateFlag(lnwire.ChanUpdateDirection),
|
Flags: lnwire.ChanUpdateFlag(lnwire.ChanUpdateDirection),
|
||||||
ChannelID: channelID,
|
ChannelID: channelID,
|
||||||
LastUpdate: time.Now(),
|
LastUpdate: testTime,
|
||||||
TimeLockDelta: testChannel.Node2.Expiry,
|
TimeLockDelta: testChannel.Node2.Expiry,
|
||||||
MinHTLC: testChannel.Node2.MinHTLC,
|
MinHTLC: testChannel.Node2.MinHTLC,
|
||||||
FeeBaseMSat: testChannel.Node2.FeeBaseMsat,
|
FeeBaseMSat: testChannel.Node2.FeeBaseMsat,
|
||||||
@ -471,13 +503,18 @@ func createTestGraph(testChannels []*testChannel) (*channeldb.ChannelGraph, func
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
|
if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
channelID++
|
channelID++
|
||||||
}
|
}
|
||||||
|
|
||||||
return graph, cleanUp, aliasMap, nil
|
return &testGraphInstance{
|
||||||
|
graph: graph,
|
||||||
|
cleanUp: cleanUp,
|
||||||
|
aliasMap: aliasMap,
|
||||||
|
privKeyMap: privKeyMap,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestFindLowestFeePath tests that out of two routes with identical total
|
// TestFindLowestFeePath tests that out of two routes with identical total
|
||||||
@ -518,13 +555,13 @@ func TestFindLowestFeePath(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
graph, cleanUp, aliases, err := createTestGraph(testChannels)
|
testGraphInstance, err := createTestGraphFromChannels(testChannels)
|
||||||
defer cleanUp()
|
defer testGraphInstance.cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceNode, err := graph.SourceNode()
|
sourceNode, err := testGraphInstance.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to fetch source node: %v", err)
|
t.Fatalf("unable to fetch source node: %v", err)
|
||||||
}
|
}
|
||||||
@ -539,10 +576,10 @@ func TestFindLowestFeePath(t *testing.T) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
||||||
target := aliases["target"]
|
target := testGraphInstance.aliasMap["target"]
|
||||||
path, err := findPath(
|
path, err := findPath(
|
||||||
nil, graph, nil, sourceNode, target, ignoredVertexes,
|
nil, testGraphInstance.graph, nil, sourceNode, target,
|
||||||
ignoredEdges, paymentAmt, noFeeLimit, nil,
|
ignoredVertexes, ignoredEdges, paymentAmt, noFeeLimit, nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find path: %v", err)
|
t.Fatalf("unable to find path: %v", err)
|
||||||
@ -556,7 +593,7 @@ func TestFindLowestFeePath(t *testing.T) {
|
|||||||
|
|
||||||
// Assert that the lowest fee route is returned.
|
// Assert that the lowest fee route is returned.
|
||||||
if !bytes.Equal(route.Hops[1].Channel.Node.PubKeyBytes[:],
|
if !bytes.Equal(route.Hops[1].Channel.Node.PubKeyBytes[:],
|
||||||
aliases["b"].SerializeCompressed()) {
|
testGraphInstance.aliasMap["b"].SerializeCompressed()) {
|
||||||
t.Fatalf("expected route to pass through b, "+
|
t.Fatalf("expected route to pass through b, "+
|
||||||
"but got a route through %v",
|
"but got a route through %v",
|
||||||
route.Hops[1].Channel.Node.Alias)
|
route.Hops[1].Channel.Node.Alias)
|
||||||
@ -626,8 +663,8 @@ var basicGraphPathFindingTests = []basicGraphPathFindingTestCase{
|
|||||||
func TestBasicGraphPathFinding(t *testing.T) {
|
func TestBasicGraphPathFinding(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
graph, cleanUp, aliases, err := parseTestGraph(basicGraphFilePath)
|
testGraphInstance, err := parseTestGraph(basicGraphFilePath)
|
||||||
defer cleanUp()
|
defer testGraphInstance.cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
@ -639,18 +676,19 @@ func TestBasicGraphPathFinding(t *testing.T) {
|
|||||||
|
|
||||||
for _, testCase := range basicGraphPathFindingTests {
|
for _, testCase := range basicGraphPathFindingTests {
|
||||||
t.Run(testCase.target, func(subT *testing.T) {
|
t.Run(testCase.target, func(subT *testing.T) {
|
||||||
testBasicGraphPathFindingCase(subT, graph, aliases, &testCase)
|
testBasicGraphPathFindingCase(subT, testGraphInstance, &testCase)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testBasicGraphPathFindingCase(t *testing.T, graph *channeldb.ChannelGraph,
|
func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstance,
|
||||||
aliases aliasMap, test *basicGraphPathFindingTestCase) {
|
test *basicGraphPathFindingTestCase) {
|
||||||
|
|
||||||
|
aliases := graphInstance.aliasMap
|
||||||
expectedHops := test.expectedHops
|
expectedHops := test.expectedHops
|
||||||
expectedHopCount := len(expectedHops)
|
expectedHopCount := len(expectedHops)
|
||||||
|
|
||||||
sourceNode, err := graph.SourceNode()
|
sourceNode, err := graphInstance.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to fetch source node: %v", err)
|
t.Fatalf("unable to fetch source node: %v", err)
|
||||||
}
|
}
|
||||||
@ -665,10 +703,10 @@ func testBasicGraphPathFindingCase(t *testing.T, graph *channeldb.ChannelGraph,
|
|||||||
)
|
)
|
||||||
|
|
||||||
paymentAmt := lnwire.NewMSatFromSatoshis(test.paymentAmt)
|
paymentAmt := lnwire.NewMSatFromSatoshis(test.paymentAmt)
|
||||||
target := aliases[test.target]
|
target := graphInstance.aliasMap[test.target]
|
||||||
path, err := findPath(
|
path, err := findPath(
|
||||||
nil, graph, nil, sourceNode, target, ignoredVertexes,
|
nil, graphInstance.graph, nil, sourceNode, target,
|
||||||
ignoredEdges, paymentAmt, test.feeLimit, nil,
|
ignoredVertexes, ignoredEdges, paymentAmt, test.feeLimit, nil,
|
||||||
)
|
)
|
||||||
if test.expectFailureNoPath {
|
if test.expectFailureNoPath {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -804,13 +842,13 @@ func testBasicGraphPathFindingCase(t *testing.T, graph *channeldb.ChannelGraph,
|
|||||||
func TestPathFindingWithAdditionalEdges(t *testing.T) {
|
func TestPathFindingWithAdditionalEdges(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
graph, cleanUp, aliases, err := parseTestGraph(basicGraphFilePath)
|
graph, err := parseTestGraph(basicGraphFilePath)
|
||||||
defer cleanUp()
|
defer graph.cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceNode, err := graph.SourceNode()
|
sourceNode, err := graph.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to fetch source node: %v", err)
|
t.Fatalf("unable to fetch source node: %v", err)
|
||||||
}
|
}
|
||||||
@ -847,12 +885,12 @@ func TestPathFindingWithAdditionalEdges(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
additionalEdges := map[Vertex][]*channeldb.ChannelEdgePolicy{
|
additionalEdges := map[Vertex][]*channeldb.ChannelEdgePolicy{
|
||||||
NewVertex(aliases["songoku"]): {songokuToDoge},
|
NewVertex(graph.aliasMap["songoku"]): {songokuToDoge},
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should now be able to find a path from roasbeef to doge.
|
// We should now be able to find a path from roasbeef to doge.
|
||||||
path, err := findPath(
|
path, err := findPath(
|
||||||
nil, graph, additionalEdges, sourceNode, dogePubKey, nil, nil,
|
nil, graph.graph, additionalEdges, sourceNode, dogePubKey, nil, nil,
|
||||||
paymentAmt, noFeeLimit, nil,
|
paymentAmt, noFeeLimit, nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -867,13 +905,13 @@ func TestPathFindingWithAdditionalEdges(t *testing.T) {
|
|||||||
func TestKShortestPathFinding(t *testing.T) {
|
func TestKShortestPathFinding(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
graph, cleanUp, aliases, err := parseTestGraph(basicGraphFilePath)
|
graph, err := parseTestGraph(basicGraphFilePath)
|
||||||
defer cleanUp()
|
defer graph.cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceNode, err := graph.SourceNode()
|
sourceNode, err := graph.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to fetch source node: %v", err)
|
t.Fatalf("unable to fetch source node: %v", err)
|
||||||
}
|
}
|
||||||
@ -887,9 +925,9 @@ func TestKShortestPathFinding(t *testing.T) {
|
|||||||
// them in order of their total "distance".
|
// them in order of their total "distance".
|
||||||
|
|
||||||
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
||||||
target := aliases["luoji"]
|
target := graph.aliasMap["luoji"]
|
||||||
paths, err := findPaths(
|
paths, err := findPaths(
|
||||||
nil, graph, sourceNode, target, paymentAmt, noFeeLimit, 100,
|
nil, graph.graph, sourceNode, target, paymentAmt, noFeeLimit, 100,
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1206,13 +1244,13 @@ func TestNewRoutePathTooLong(t *testing.T) {
|
|||||||
|
|
||||||
// Ensure that potential paths which are over the maximum hop-limit are
|
// Ensure that potential paths which are over the maximum hop-limit are
|
||||||
// rejected.
|
// rejected.
|
||||||
graph, cleanUp, aliases, err := parseTestGraph(excessiveHopsGraphFilePath)
|
graph, err := parseTestGraph(excessiveHopsGraphFilePath)
|
||||||
defer cleanUp()
|
defer graph.cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceNode, err := graph.SourceNode()
|
sourceNode, err := graph.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to fetch source node: %v", err)
|
t.Fatalf("unable to fetch source node: %v", err)
|
||||||
}
|
}
|
||||||
@ -1224,9 +1262,9 @@ func TestNewRoutePathTooLong(t *testing.T) {
|
|||||||
|
|
||||||
// We start by confirming that routing a payment 20 hops away is possible.
|
// We start by confirming that routing a payment 20 hops away is possible.
|
||||||
// Alice should be able to find a valid route to ursula.
|
// Alice should be able to find a valid route to ursula.
|
||||||
target := aliases["ursula"]
|
target := graph.aliasMap["ursula"]
|
||||||
_, err = findPath(
|
_, err = findPath(
|
||||||
nil, graph, nil, sourceNode, target, ignoredVertexes,
|
nil, graph.graph, nil, sourceNode, target, ignoredVertexes,
|
||||||
ignoredEdges, paymentAmt, noFeeLimit, nil,
|
ignoredEdges, paymentAmt, noFeeLimit, nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1235,9 +1273,9 @@ func TestNewRoutePathTooLong(t *testing.T) {
|
|||||||
|
|
||||||
// Vincent is 21 hops away from Alice, and thus no valid route should be
|
// Vincent is 21 hops away from Alice, and thus no valid route should be
|
||||||
// presented to Alice.
|
// presented to Alice.
|
||||||
target = aliases["vincent"]
|
target = graph.aliasMap["vincent"]
|
||||||
path, err := findPath(
|
path, err := findPath(
|
||||||
nil, graph, nil, sourceNode, target, ignoredVertexes,
|
nil, graph.graph, nil, sourceNode, target, ignoredVertexes,
|
||||||
ignoredEdges, paymentAmt, noFeeLimit, nil,
|
ignoredEdges, paymentAmt, noFeeLimit, nil,
|
||||||
)
|
)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -1251,13 +1289,13 @@ func TestNewRoutePathTooLong(t *testing.T) {
|
|||||||
func TestPathNotAvailable(t *testing.T) {
|
func TestPathNotAvailable(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
graph, cleanUp, _, err := parseTestGraph(basicGraphFilePath)
|
graph, err := parseTestGraph(basicGraphFilePath)
|
||||||
defer cleanUp()
|
defer graph.cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceNode, err := graph.SourceNode()
|
sourceNode, err := graph.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to fetch source node: %v", err)
|
t.Fatalf("unable to fetch source node: %v", err)
|
||||||
}
|
}
|
||||||
@ -1279,7 +1317,7 @@ func TestPathNotAvailable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err = findPath(
|
_, err = findPath(
|
||||||
nil, graph, nil, sourceNode, unknownNode, ignoredVertexes,
|
nil, graph.graph, nil, sourceNode, unknownNode, ignoredVertexes,
|
||||||
ignoredEdges, 100, noFeeLimit, nil,
|
ignoredEdges, 100, noFeeLimit, nil,
|
||||||
)
|
)
|
||||||
if !IsError(err, ErrNoPathFound) {
|
if !IsError(err, ErrNoPathFound) {
|
||||||
@ -1290,13 +1328,13 @@ func TestPathNotAvailable(t *testing.T) {
|
|||||||
func TestPathInsufficientCapacity(t *testing.T) {
|
func TestPathInsufficientCapacity(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
graph, cleanUp, aliases, err := parseTestGraph(basicGraphFilePath)
|
graph, err := parseTestGraph(basicGraphFilePath)
|
||||||
defer cleanUp()
|
defer graph.cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceNode, err := graph.SourceNode()
|
sourceNode, err := graph.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to fetch source node: %v", err)
|
t.Fatalf("unable to fetch source node: %v", err)
|
||||||
}
|
}
|
||||||
@ -1311,11 +1349,11 @@ func TestPathInsufficientCapacity(t *testing.T) {
|
|||||||
// satoshis. The largest channel in the basic graph is of size 100k
|
// satoshis. The largest channel in the basic graph is of size 100k
|
||||||
// satoshis, so we shouldn't be able to find a path to sophon even
|
// satoshis, so we shouldn't be able to find a path to sophon even
|
||||||
// though we have a 2-hop link.
|
// though we have a 2-hop link.
|
||||||
target := aliases["sophon"]
|
target := graph.aliasMap["sophon"]
|
||||||
|
|
||||||
payAmt := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
|
payAmt := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
|
||||||
_, err = findPath(
|
_, err = findPath(
|
||||||
nil, graph, nil, sourceNode, target, ignoredVertexes,
|
nil, graph.graph, nil, sourceNode, target, ignoredVertexes,
|
||||||
ignoredEdges, payAmt, noFeeLimit, nil,
|
ignoredEdges, payAmt, noFeeLimit, nil,
|
||||||
)
|
)
|
||||||
if !IsError(err, ErrNoPathFound) {
|
if !IsError(err, ErrNoPathFound) {
|
||||||
@ -1328,13 +1366,13 @@ func TestPathInsufficientCapacity(t *testing.T) {
|
|||||||
func TestRouteFailMinHTLC(t *testing.T) {
|
func TestRouteFailMinHTLC(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
graph, cleanUp, aliases, err := parseTestGraph(basicGraphFilePath)
|
graph, err := parseTestGraph(basicGraphFilePath)
|
||||||
defer cleanUp()
|
defer graph.cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceNode, err := graph.SourceNode()
|
sourceNode, err := graph.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to fetch source node: %v", err)
|
t.Fatalf("unable to fetch source node: %v", err)
|
||||||
}
|
}
|
||||||
@ -1344,10 +1382,10 @@ func TestRouteFailMinHTLC(t *testing.T) {
|
|||||||
// We'll not attempt to route an HTLC of 10 SAT from roasbeef to Son
|
// We'll not attempt to route an HTLC of 10 SAT from roasbeef to Son
|
||||||
// Goku. However, the min HTLC of Son Goku is 1k SAT, as a result, this
|
// Goku. However, the min HTLC of Son Goku is 1k SAT, as a result, this
|
||||||
// attempt should fail.
|
// attempt should fail.
|
||||||
target := aliases["songoku"]
|
target := graph.aliasMap["songoku"]
|
||||||
payAmt := lnwire.MilliSatoshi(10)
|
payAmt := lnwire.MilliSatoshi(10)
|
||||||
_, err = findPath(
|
_, err = findPath(
|
||||||
nil, graph, nil, sourceNode, target, ignoredVertexes,
|
nil, graph.graph, nil, sourceNode, target, ignoredVertexes,
|
||||||
ignoredEdges, payAmt, noFeeLimit, nil,
|
ignoredEdges, payAmt, noFeeLimit, nil,
|
||||||
)
|
)
|
||||||
if !IsError(err, ErrNoPathFound) {
|
if !IsError(err, ErrNoPathFound) {
|
||||||
@ -1361,13 +1399,13 @@ func TestRouteFailMinHTLC(t *testing.T) {
|
|||||||
func TestRouteFailDisabledEdge(t *testing.T) {
|
func TestRouteFailDisabledEdge(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
graph, cleanUp, aliases, err := parseTestGraph(basicGraphFilePath)
|
graph, err := parseTestGraph(basicGraphFilePath)
|
||||||
defer cleanUp()
|
defer graph.cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceNode, err := graph.SourceNode()
|
sourceNode, err := graph.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to fetch source node: %v", err)
|
t.Fatalf("unable to fetch source node: %v", err)
|
||||||
}
|
}
|
||||||
@ -1376,10 +1414,10 @@ func TestRouteFailDisabledEdge(t *testing.T) {
|
|||||||
|
|
||||||
// First, we'll try to route from roasbeef -> sophon. This should
|
// First, we'll try to route from roasbeef -> sophon. This should
|
||||||
// succeed without issue, and return a single path via phamnuwen
|
// succeed without issue, and return a single path via phamnuwen
|
||||||
target := aliases["sophon"]
|
target := graph.aliasMap["sophon"]
|
||||||
payAmt := lnwire.NewMSatFromSatoshis(105000)
|
payAmt := lnwire.NewMSatFromSatoshis(105000)
|
||||||
_, err = findPath(
|
_, err = findPath(
|
||||||
nil, graph, nil, sourceNode, target, ignoredVertexes,
|
nil, graph.graph, nil, sourceNode, target, ignoredVertexes,
|
||||||
ignoredEdges, payAmt, noFeeLimit, nil,
|
ignoredEdges, payAmt, noFeeLimit, nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1388,19 +1426,19 @@ func TestRouteFailDisabledEdge(t *testing.T) {
|
|||||||
|
|
||||||
// First, we'll modify the edge from roasbeef -> phamnuwen, to read that
|
// First, we'll modify the edge from roasbeef -> phamnuwen, to read that
|
||||||
// it's disabled.
|
// it's disabled.
|
||||||
_, _, phamnuwenEdge, err := graph.FetchChannelEdgesByID(999991)
|
_, _, phamnuwenEdge, err := graph.graph.FetchChannelEdgesByID(999991)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to fetch goku's edge: %v", err)
|
t.Fatalf("unable to fetch goku's edge: %v", err)
|
||||||
}
|
}
|
||||||
phamnuwenEdge.Flags = lnwire.ChanUpdateDisabled | lnwire.ChanUpdateDirection
|
phamnuwenEdge.Flags = lnwire.ChanUpdateDisabled | lnwire.ChanUpdateDirection
|
||||||
if err := graph.UpdateEdgePolicy(phamnuwenEdge); err != nil {
|
if err := graph.graph.UpdateEdgePolicy(phamnuwenEdge); err != nil {
|
||||||
t.Fatalf("unable to update edge: %v", err)
|
t.Fatalf("unable to update edge: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, if we attempt to route through that edge, we should get a
|
// Now, if we attempt to route through that edge, we should get a
|
||||||
// failure as it is no longer eligible.
|
// failure as it is no longer eligible.
|
||||||
_, err = findPath(
|
_, err = findPath(
|
||||||
nil, graph, nil, sourceNode, target, ignoredVertexes,
|
nil, graph.graph, nil, sourceNode, target, ignoredVertexes,
|
||||||
ignoredEdges, payAmt, noFeeLimit, nil,
|
ignoredEdges, payAmt, noFeeLimit, nil,
|
||||||
)
|
)
|
||||||
if !IsError(err, ErrNoPathFound) {
|
if !IsError(err, ErrNoPathFound) {
|
||||||
@ -1425,7 +1463,7 @@ func TestPathFindSpecExample(t *testing.T) {
|
|||||||
// we'll pass that in to ensure that the router uses 100 as the current
|
// we'll pass that in to ensure that the router uses 100 as the current
|
||||||
// height.
|
// height.
|
||||||
const startingHeight = 100
|
const startingHeight = 100
|
||||||
ctx, cleanUp, err := createTestCtx(startingHeight, specExampleFilePath)
|
ctx, cleanUp, err := createTestCtxFromFile(startingHeight, specExampleFilePath)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
|
@ -1801,7 +1801,8 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
// correct block height is.
|
// correct block height is.
|
||||||
case *lnwire.FailExpiryTooSoon:
|
case *lnwire.FailExpiryTooSoon:
|
||||||
update := onionErr.Update
|
update := onionErr.Update
|
||||||
if err := r.applyChannelUpdate(&update); err != nil {
|
err := r.applyChannelUpdate(&update, errSource)
|
||||||
|
if err != nil {
|
||||||
log.Errorf("unable to apply channel "+
|
log.Errorf("unable to apply channel "+
|
||||||
"update for onion error: %v", err)
|
"update for onion error: %v", err)
|
||||||
}
|
}
|
||||||
@ -1826,7 +1827,8 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
// and continue with the rest of the routes.
|
// and continue with the rest of the routes.
|
||||||
case *lnwire.FailAmountBelowMinimum:
|
case *lnwire.FailAmountBelowMinimum:
|
||||||
update := onionErr.Update
|
update := onionErr.Update
|
||||||
if err := r.applyChannelUpdate(&update); err != nil {
|
err := r.applyChannelUpdate(&update, errSource)
|
||||||
|
if err != nil {
|
||||||
log.Errorf("unable to apply channel "+
|
log.Errorf("unable to apply channel "+
|
||||||
"update for onion error: %v", err)
|
"update for onion error: %v", err)
|
||||||
}
|
}
|
||||||
@ -1838,7 +1840,8 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
// newly updated fees.
|
// newly updated fees.
|
||||||
case *lnwire.FailFeeInsufficient:
|
case *lnwire.FailFeeInsufficient:
|
||||||
update := onionErr.Update
|
update := onionErr.Update
|
||||||
if err := r.applyChannelUpdate(&update); err != nil {
|
err := r.applyChannelUpdate(&update, errSource)
|
||||||
|
if err != nil {
|
||||||
log.Errorf("unable to apply channel "+
|
log.Errorf("unable to apply channel "+
|
||||||
"update for onion error: %v", err)
|
"update for onion error: %v", err)
|
||||||
|
|
||||||
@ -1871,7 +1874,8 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
// finding.
|
// finding.
|
||||||
case *lnwire.FailIncorrectCltvExpiry:
|
case *lnwire.FailIncorrectCltvExpiry:
|
||||||
update := onionErr.Update
|
update := onionErr.Update
|
||||||
if err := r.applyChannelUpdate(&update); err != nil {
|
err := r.applyChannelUpdate(&update, errSource)
|
||||||
|
if err != nil {
|
||||||
log.Errorf("unable to apply channel "+
|
log.Errorf("unable to apply channel "+
|
||||||
"update for onion error: %v", err)
|
"update for onion error: %v", err)
|
||||||
}
|
}
|
||||||
@ -1886,7 +1890,8 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
// the update and continue.
|
// the update and continue.
|
||||||
case *lnwire.FailChannelDisabled:
|
case *lnwire.FailChannelDisabled:
|
||||||
update := onionErr.Update
|
update := onionErr.Update
|
||||||
if err := r.applyChannelUpdate(&update); err != nil {
|
err := r.applyChannelUpdate(&update, errSource)
|
||||||
|
if err != nil {
|
||||||
log.Errorf("unable to apply channel "+
|
log.Errorf("unable to apply channel "+
|
||||||
"update for onion error: %v", err)
|
"update for onion error: %v", err)
|
||||||
}
|
}
|
||||||
@ -1899,7 +1904,8 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
// now, and continue onwards with our path finding.
|
// now, and continue onwards with our path finding.
|
||||||
case *lnwire.FailTemporaryChannelFailure:
|
case *lnwire.FailTemporaryChannelFailure:
|
||||||
update := onionErr.Update
|
update := onionErr.Update
|
||||||
if err := r.applyChannelUpdate(update); err != nil {
|
err := r.applyChannelUpdate(update, errSource)
|
||||||
|
if err != nil {
|
||||||
log.Errorf("unable to apply channel "+
|
log.Errorf("unable to apply channel "+
|
||||||
"update for onion error: %v", err)
|
"update for onion error: %v", err)
|
||||||
}
|
}
|
||||||
@ -2023,15 +2029,20 @@ func pruneEdgeFailure(paySession *paymentSession, route *Route,
|
|||||||
paySession.ReportChannelFailure(badChan.ChannelID)
|
paySession.ReportChannelFailure(badChan.ChannelID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyChannelUpdate applies a channel update directly to the database,
|
// applyChannelUpdate validates a channel update and if valid, applies it to the
|
||||||
// skipping preliminary validation.
|
// database.
|
||||||
func (r *ChannelRouter) applyChannelUpdate(msg *lnwire.ChannelUpdate) error {
|
func (r *ChannelRouter) applyChannelUpdate(msg *lnwire.ChannelUpdate,
|
||||||
|
pubKey *btcec.PublicKey) error {
|
||||||
// If we get passed a nil channel update (as it's optional with some
|
// If we get passed a nil channel update (as it's optional with some
|
||||||
// onion errors), then we'll exit early with a nil error.
|
// onion errors), then we'll exit early with a nil error.
|
||||||
if msg == nil {
|
if msg == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := ValidateChannelUpdateAnn(pubKey, msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
err := r.UpdateEdge(&channeldb.ChannelEdgePolicy{
|
err := r.UpdateEdge(&channeldb.ChannelEdgePolicy{
|
||||||
SigBytes: msg.Signature.ToSignatureBytes(),
|
SigBytes: msg.Signature.ToSignatureBytes(),
|
||||||
ChannelID: msg.ShortChannelID.ToUint64(),
|
ChannelID: msg.ShortChannelID.ToUint64(),
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/btcsuite/btcd/btcec"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/lightningnetwork/lightning-onion"
|
"github.com/lightningnetwork/lightning-onion"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
@ -74,51 +75,17 @@ func copyPubKey(pub *btcec.PublicKey) *btcec.PublicKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTestCtx(startingHeight uint32, testGraph ...string) (*testCtx, func(), error) {
|
func createTestCtxFromGraphInstance(startingHeight uint32, graphInstance *testGraphInstance) (
|
||||||
var (
|
*testCtx, func(), error) {
|
||||||
graph *channeldb.ChannelGraph
|
|
||||||
sourceNode *channeldb.LightningNode
|
|
||||||
cleanup func()
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
aliasMap := make(map[string]*btcec.PublicKey)
|
// We'll initialize an instance of the channel router with mock
|
||||||
|
|
||||||
// If the testGraph isn't set, then we'll create an empty graph to
|
|
||||||
// start out with. Our usage of a variadic parameter allows caller to
|
|
||||||
// omit the testGraph argument all together if they wish to start with
|
|
||||||
// a blank graph.
|
|
||||||
if testGraph == nil {
|
|
||||||
// First we'll set up a test graph for usage within the test.
|
|
||||||
graph, cleanup, err = makeTestGraph()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("unable to create test graph: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceNode, err = createTestNode()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("unable to create source node: %v", err)
|
|
||||||
}
|
|
||||||
if err = graph.SetSourceNode(sourceNode); err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("unable to set source node: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Otherwise, we'll attempt to locate and parse out the file
|
|
||||||
// that encodes the graph that our tests should be run against.
|
|
||||||
graph, cleanup, aliasMap, err = parseTestGraph(testGraph[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("unable to create test graph: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next we'll initialize an instance of the channel router with mock
|
|
||||||
// versions of the chain and channel notifier. As we don't need to test
|
// versions of the chain and channel notifier. As we don't need to test
|
||||||
// any p2p functionality, the peer send and switch send messages won't
|
// any p2p functionality, the peer send and switch send messages won't
|
||||||
// be populated.
|
// be populated.
|
||||||
chain := newMockChain(startingHeight)
|
chain := newMockChain(startingHeight)
|
||||||
chainView := newMockChainView(chain)
|
chainView := newMockChainView(chain)
|
||||||
router, err := New(Config{
|
router, err := New(Config{
|
||||||
Graph: graph,
|
Graph: graphInstance.graph,
|
||||||
Chain: chain,
|
Chain: chain,
|
||||||
ChainView: chainView,
|
ChainView: chainView,
|
||||||
SendToSwitch: func(_ lnwire.ShortChannelID,
|
SendToSwitch: func(_ lnwire.ShortChannelID,
|
||||||
@ -141,20 +108,60 @@ func createTestCtx(startingHeight uint32, testGraph ...string) (*testCtx, func()
|
|||||||
|
|
||||||
ctx := &testCtx{
|
ctx := &testCtx{
|
||||||
router: router,
|
router: router,
|
||||||
graph: graph,
|
graph: graphInstance.graph,
|
||||||
aliases: aliasMap,
|
aliases: graphInstance.aliasMap,
|
||||||
chain: chain,
|
chain: chain,
|
||||||
chainView: chainView,
|
chainView: chainView,
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanUp := func() {
|
cleanUp := func() {
|
||||||
ctx.router.Stop()
|
ctx.router.Stop()
|
||||||
cleanup()
|
graphInstance.cleanUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx, cleanUp, nil
|
return ctx, cleanUp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createTestCtxSingleNode(startingHeight uint32) (*testCtx, func(), error) {
|
||||||
|
var (
|
||||||
|
graph *channeldb.ChannelGraph
|
||||||
|
sourceNode *channeldb.LightningNode
|
||||||
|
cleanup func()
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
graph, cleanup, err = makeTestGraph()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("unable to create test graph: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceNode, err = createTestNode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("unable to create source node: %v", err)
|
||||||
|
}
|
||||||
|
if err = graph.SetSourceNode(sourceNode); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("unable to set source node: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
graphInstance := &testGraphInstance{
|
||||||
|
graph: graph,
|
||||||
|
cleanUp: cleanup,
|
||||||
|
}
|
||||||
|
|
||||||
|
return createTestCtxFromGraphInstance(startingHeight, graphInstance)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTestCtxFromFile(startingHeight uint32, testGraph string) (*testCtx, func(), error) {
|
||||||
|
// We'll attempt to locate and parse out the file
|
||||||
|
// that encodes the graph that our tests should be run against.
|
||||||
|
graphInstance, err := parseTestGraph(testGraph)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("unable to create test graph: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return createTestCtxFromGraphInstance(startingHeight, graphInstance)
|
||||||
|
}
|
||||||
|
|
||||||
// TestFindRoutesFeeSorting asserts that routes found by the FindRoutes method
|
// TestFindRoutesFeeSorting asserts that routes found by the FindRoutes method
|
||||||
// within the channel router are properly returned in a sorted order, with the
|
// within the channel router are properly returned in a sorted order, with the
|
||||||
// lowest fee route coming first.
|
// lowest fee route coming first.
|
||||||
@ -162,7 +169,7 @@ func TestFindRoutesFeeSorting(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight, basicGraphFilePath)
|
ctx, cleanUp, err := createTestCtxFromFile(startingBlockHeight, basicGraphFilePath)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
@ -215,7 +222,7 @@ func TestFindRoutesWithFeeLimit(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(
|
ctx, cleanUp, err := createTestCtxFromFile(
|
||||||
startingBlockHeight, basicGraphFilePath,
|
startingBlockHeight, basicGraphFilePath,
|
||||||
)
|
)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
@ -269,7 +276,7 @@ func TestSendPaymentRouteFailureFallback(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight, basicGraphFilePath)
|
ctx, cleanUp, err := createTestCtxFromFile(startingBlockHeight, basicGraphFilePath)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
@ -341,6 +348,171 @@ func TestSendPaymentRouteFailureFallback(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestChannelUpdateValidation tests that a failed payment with an associated
|
||||||
|
// channel update will only be applied to the graph when the update contains a
|
||||||
|
// valid signature.
|
||||||
|
func TestChannelUpdateValidation(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Setup a three node network.
|
||||||
|
testChannels := []*testChannel{
|
||||||
|
symmetricTestChannel("a", "b", 100000, &testChannelPolicy{
|
||||||
|
Expiry: 144,
|
||||||
|
FeeRate: 400,
|
||||||
|
MinHTLC: 1,
|
||||||
|
}, 1),
|
||||||
|
symmetricTestChannel("b", "c", 100000, &testChannelPolicy{
|
||||||
|
Expiry: 144,
|
||||||
|
FeeRate: 400,
|
||||||
|
MinHTLC: 1,
|
||||||
|
}, 2),
|
||||||
|
}
|
||||||
|
|
||||||
|
testGraph, err := createTestGraphFromChannels(testChannels)
|
||||||
|
defer testGraph.cleanUp()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
const startingBlockHeight = 101
|
||||||
|
|
||||||
|
ctx, cleanUp, err := createTestCtxFromGraphInstance(startingBlockHeight,
|
||||||
|
testGraph)
|
||||||
|
|
||||||
|
defer cleanUp()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create router: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that the initially configured fee is retrieved correctly.
|
||||||
|
_, policy, _, err := ctx.router.GetChannelByID(
|
||||||
|
lnwire.NewShortChanIDFromInt(1))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot retrieve channel")
|
||||||
|
}
|
||||||
|
|
||||||
|
if policy.FeeProportionalMillionths != 400 {
|
||||||
|
t.Fatalf("invalid fee")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup a route from source a to destination c. The route will be used
|
||||||
|
// in a call to SendToRoute. SendToRoute also applies channel updates,
|
||||||
|
// but it saves us from including RequestRoute in the test scope too.
|
||||||
|
var hop1 [33]byte
|
||||||
|
copy(hop1[:], ctx.aliases["b"].SerializeCompressed())
|
||||||
|
|
||||||
|
var hop2 [33]byte
|
||||||
|
copy(hop2[:], ctx.aliases["c"].SerializeCompressed())
|
||||||
|
|
||||||
|
hops := []*Hop{
|
||||||
|
{
|
||||||
|
Channel: &ChannelHop{
|
||||||
|
ChannelEdgePolicy: &channeldb.ChannelEdgePolicy{
|
||||||
|
ChannelID: 1,
|
||||||
|
Node: &channeldb.LightningNode{
|
||||||
|
PubKeyBytes: hop1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Channel: &ChannelHop{
|
||||||
|
ChannelEdgePolicy: &channeldb.ChannelEdgePolicy{
|
||||||
|
ChannelID: 2,
|
||||||
|
Node: &channeldb.LightningNode{
|
||||||
|
PubKeyBytes: hop2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
route := &Route{
|
||||||
|
Hops: hops,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up a channel update message with an invalid signature to be
|
||||||
|
// returned to the sender.
|
||||||
|
var invalidSignature [64]byte
|
||||||
|
errChanUpdate := lnwire.ChannelUpdate{
|
||||||
|
Signature: invalidSignature,
|
||||||
|
FeeRate: 500,
|
||||||
|
ShortChannelID: lnwire.NewShortChanIDFromInt(1),
|
||||||
|
Timestamp: uint32(testTime.Add(time.Minute).Unix()),
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll modify the SendToSwitch method so that it simulates a failed
|
||||||
|
// payment with an error originating from the first hop of the route.
|
||||||
|
// The unsigned channel update is attached to the failure message.
|
||||||
|
ctx.router.cfg.SendToSwitch = func(firstHop lnwire.ShortChannelID,
|
||||||
|
_ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) {
|
||||||
|
|
||||||
|
return [32]byte{}, &htlcswitch.ForwardingError{
|
||||||
|
ErrorSource: ctx.aliases["b"],
|
||||||
|
FailureMessage: &lnwire.FailFeeInsufficient{
|
||||||
|
Update: errChanUpdate,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The payment parameter is mostly redundant in SendToRoute. Can be left
|
||||||
|
// empty for this test.
|
||||||
|
payment := &LightningPayment{}
|
||||||
|
|
||||||
|
// Send off the payment request to the router. The specified route
|
||||||
|
// should be attempted and the channel update should be received by
|
||||||
|
// router and ignored because it is missing a valid signature.
|
||||||
|
_, _, err = ctx.router.SendToRoute([]*Route{route}, payment)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected route to fail with channel update")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, policy, _, err = ctx.router.GetChannelByID(
|
||||||
|
lnwire.NewShortChanIDFromInt(1))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot retrieve channel")
|
||||||
|
}
|
||||||
|
|
||||||
|
if policy.FeeProportionalMillionths != 400 {
|
||||||
|
t.Fatalf("fee updated without valid signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, add a signature to the channel update.
|
||||||
|
chanUpdateMsg, err := errChanUpdate.DataToSign()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
digest := chainhash.DoubleHashB(chanUpdateMsg)
|
||||||
|
sig, err := testGraph.privKeyMap["b"].Sign(digest)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
errChanUpdate.Signature, err = lnwire.NewSigFromSignature(sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retry the payment using the same route as before.
|
||||||
|
_, _, err = ctx.router.SendToRoute([]*Route{route}, payment)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected route to fail with channel update")
|
||||||
|
}
|
||||||
|
|
||||||
|
// This time a valid signature was supplied and the policy change should
|
||||||
|
// have been applied to the graph.
|
||||||
|
_, policy, _, err = ctx.router.GetChannelByID(
|
||||||
|
lnwire.NewShortChanIDFromInt(1))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot retrieve channel")
|
||||||
|
}
|
||||||
|
|
||||||
|
if policy.FeeProportionalMillionths != 500 {
|
||||||
|
t.Fatalf("fee not updated even though signature is valid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestSendPaymentErrorRepeatedFeeInsufficient tests that if we receive
|
// TestSendPaymentErrorRepeatedFeeInsufficient tests that if we receive
|
||||||
// multiple fee related errors from a channel that we're attempting to route
|
// multiple fee related errors from a channel that we're attempting to route
|
||||||
// through, then we'll prune the channel after the second attempt.
|
// through, then we'll prune the channel after the second attempt.
|
||||||
@ -348,7 +520,7 @@ func TestSendPaymentErrorRepeatedFeeInsufficient(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight, basicGraphFilePath)
|
ctx, cleanUp, err := createTestCtxFromFile(startingBlockHeight, basicGraphFilePath)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
@ -449,7 +621,7 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight, basicGraphFilePath)
|
ctx, cleanUp, err := createTestCtxFromFile(startingBlockHeight, basicGraphFilePath)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
@ -581,7 +753,7 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight, basicGraphFilePath)
|
ctx, cleanUp, err := createTestCtxFromFile(startingBlockHeight, basicGraphFilePath)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
@ -753,7 +925,7 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
|
|||||||
func TestAddProof(t *testing.T) {
|
func TestAddProof(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
ctx, cleanup, err := createTestCtx(0)
|
ctx, cleanup, err := createTestCtxSingleNode(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -816,7 +988,7 @@ func TestIgnoreNodeAnnouncement(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight,
|
ctx, cleanUp, err := createTestCtxFromFile(startingBlockHeight,
|
||||||
basicGraphFilePath)
|
basicGraphFilePath)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -849,7 +1021,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight,
|
ctx, cleanUp, err := createTestCtxFromFile(startingBlockHeight,
|
||||||
basicGraphFilePath)
|
basicGraphFilePath)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -910,7 +1082,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
|||||||
edgePolicy := &channeldb.ChannelEdgePolicy{
|
edgePolicy := &channeldb.ChannelEdgePolicy{
|
||||||
SigBytes: testSig.Serialize(),
|
SigBytes: testSig.Serialize(),
|
||||||
ChannelID: edge.ChannelID,
|
ChannelID: edge.ChannelID,
|
||||||
LastUpdate: time.Now(),
|
LastUpdate: testTime,
|
||||||
TimeLockDelta: 10,
|
TimeLockDelta: 10,
|
||||||
MinHTLC: 1,
|
MinHTLC: 1,
|
||||||
FeeBaseMSat: 10,
|
FeeBaseMSat: 10,
|
||||||
@ -926,7 +1098,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
|||||||
edgePolicy = &channeldb.ChannelEdgePolicy{
|
edgePolicy = &channeldb.ChannelEdgePolicy{
|
||||||
SigBytes: testSig.Serialize(),
|
SigBytes: testSig.Serialize(),
|
||||||
ChannelID: edge.ChannelID,
|
ChannelID: edge.ChannelID,
|
||||||
LastUpdate: time.Now(),
|
LastUpdate: testTime,
|
||||||
TimeLockDelta: 10,
|
TimeLockDelta: 10,
|
||||||
MinHTLC: 1,
|
MinHTLC: 1,
|
||||||
FeeBaseMSat: 10,
|
FeeBaseMSat: 10,
|
||||||
@ -1006,7 +1178,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
|||||||
edgePolicy = &channeldb.ChannelEdgePolicy{
|
edgePolicy = &channeldb.ChannelEdgePolicy{
|
||||||
SigBytes: testSig.Serialize(),
|
SigBytes: testSig.Serialize(),
|
||||||
ChannelID: edge.ChannelID,
|
ChannelID: edge.ChannelID,
|
||||||
LastUpdate: time.Now(),
|
LastUpdate: testTime,
|
||||||
TimeLockDelta: 10,
|
TimeLockDelta: 10,
|
||||||
MinHTLC: 1,
|
MinHTLC: 1,
|
||||||
FeeBaseMSat: 10,
|
FeeBaseMSat: 10,
|
||||||
@ -1021,7 +1193,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
|||||||
edgePolicy = &channeldb.ChannelEdgePolicy{
|
edgePolicy = &channeldb.ChannelEdgePolicy{
|
||||||
SigBytes: testSig.Serialize(),
|
SigBytes: testSig.Serialize(),
|
||||||
ChannelID: edge.ChannelID,
|
ChannelID: edge.ChannelID,
|
||||||
LastUpdate: time.Now(),
|
LastUpdate: testTime,
|
||||||
TimeLockDelta: 10,
|
TimeLockDelta: 10,
|
||||||
MinHTLC: 1,
|
MinHTLC: 1,
|
||||||
FeeBaseMSat: 10,
|
FeeBaseMSat: 10,
|
||||||
@ -1119,7 +1291,7 @@ func TestWakeUpOnStaleBranch(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight)
|
ctx, cleanUp, err := createTestCtxSingleNode(startingBlockHeight)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
@ -1322,7 +1494,7 @@ func TestDisconnectedBlocks(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight)
|
ctx, cleanUp, err := createTestCtxSingleNode(startingBlockHeight)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
@ -1512,7 +1684,7 @@ func TestRouterChansClosedOfflinePruneGraph(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight)
|
ctx, cleanUp, err := createTestCtxSingleNode(startingBlockHeight)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
@ -1665,7 +1837,7 @@ func TestFindPathFeeWeighting(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight, basicGraphFilePath)
|
ctx, cleanUp, err := createTestCtxFromFile(startingBlockHeight, basicGraphFilePath)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
@ -1716,7 +1888,7 @@ func TestIsStaleNode(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight)
|
ctx, cleanUp, err := createTestCtxSingleNode(startingBlockHeight)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
@ -1798,7 +1970,7 @@ func TestIsKnownEdge(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight)
|
ctx, cleanUp, err := createTestCtxSingleNode(startingBlockHeight)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
@ -1850,7 +2022,7 @@ func TestIsStaleEdgePolicy(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtx(startingBlockHeight,
|
ctx, cleanUp, err := createTestCtxFromFile(startingBlockHeight,
|
||||||
basicGraphFilePath)
|
basicGraphFilePath)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user