mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
2657 lines
72 KiB
Go
2657 lines
72 KiB
Go
package discovery
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math/big"
|
|
prand "math/rand"
|
|
"net"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/btcsuite/btcd/btcec"
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/btcsuite/btcutil"
|
|
"github.com/davecgh/go-spew/spew"
|
|
"github.com/go-errors/errors"
|
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
|
"github.com/lightningnetwork/lnd/lnpeer"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
"github.com/lightningnetwork/lnd/routing"
|
|
)
|
|
|
|
var (
|
|
testAddr = &net.TCPAddr{IP: (net.IP)([]byte{0xA, 0x0, 0x0, 0x1}),
|
|
Port: 9000}
|
|
testAddrs = []net.Addr{testAddr}
|
|
testFeatures = lnwire.NewRawFeatureVector()
|
|
testSig = &btcec.Signature{
|
|
R: new(big.Int),
|
|
S: new(big.Int),
|
|
}
|
|
_, _ = testSig.R.SetString("63724406601629180062774974542967536251589935445068131219452686511677818569431", 10)
|
|
_, _ = testSig.S.SetString("18801056069249825825291287104931333862866033135609736119018462340006816851118", 10)
|
|
|
|
inputStr = "147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b"
|
|
sha, _ = chainhash.NewHashFromStr(inputStr)
|
|
outpoint = wire.NewOutPoint(sha, 0)
|
|
|
|
bitcoinKeyPriv1, _ = btcec.NewPrivateKey(btcec.S256())
|
|
bitcoinKeyPub1 = bitcoinKeyPriv1.PubKey()
|
|
|
|
nodeKeyPriv1, _ = btcec.NewPrivateKey(btcec.S256())
|
|
nodeKeyPub1 = nodeKeyPriv1.PubKey()
|
|
|
|
bitcoinKeyPriv2, _ = btcec.NewPrivateKey(btcec.S256())
|
|
bitcoinKeyPub2 = bitcoinKeyPriv2.PubKey()
|
|
|
|
nodeKeyPriv2, _ = btcec.NewPrivateKey(btcec.S256())
|
|
nodeKeyPub2 = nodeKeyPriv2.PubKey()
|
|
|
|
trickleDelay = time.Millisecond * 100
|
|
retransmitDelay = time.Hour * 1
|
|
proofMatureDelta uint32
|
|
maxBtcFundingAmount = btcutil.Amount(1<<62) - 1
|
|
)
|
|
|
|
// makeTestDB creates a new instance of the ChannelDB for testing purposes. A
|
|
// callback which cleans up the created temporary directories is also returned
|
|
// and intended to be executed after the test completes.
|
|
func makeTestDB() (*channeldb.DB, func(), error) {
|
|
// First, create a temporary directory to be used for the duration of
|
|
// this test.
|
|
tempDirName, err := ioutil.TempDir("", "channeldb")
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Next, create channeldb for the first time.
|
|
cdb, err := channeldb.Open(tempDirName)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
cleanUp := func() {
|
|
cdb.Close()
|
|
os.RemoveAll(tempDirName)
|
|
}
|
|
|
|
return cdb, cleanUp, nil
|
|
}
|
|
|
|
type mockSigner struct {
|
|
privKey *btcec.PrivateKey
|
|
}
|
|
|
|
func (n *mockSigner) SignMessage(pubKey *btcec.PublicKey,
|
|
msg []byte) (*btcec.Signature, error) {
|
|
|
|
if !pubKey.IsEqual(n.privKey.PubKey()) {
|
|
return nil, fmt.Errorf("unknown public key")
|
|
}
|
|
|
|
digest := chainhash.DoubleHashB(msg)
|
|
sign, err := n.privKey.Sign(digest)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("can't sign the message: %v", err)
|
|
}
|
|
|
|
return sign, nil
|
|
}
|
|
|
|
type mockGraphSource struct {
|
|
bestHeight uint32
|
|
|
|
mu sync.Mutex
|
|
nodes []channeldb.LightningNode
|
|
infos map[uint64]channeldb.ChannelEdgeInfo
|
|
edges map[uint64][]channeldb.ChannelEdgePolicy
|
|
}
|
|
|
|
func newMockRouter(height uint32) *mockGraphSource {
|
|
return &mockGraphSource{
|
|
bestHeight: height,
|
|
infos: make(map[uint64]channeldb.ChannelEdgeInfo),
|
|
edges: make(map[uint64][]channeldb.ChannelEdgePolicy),
|
|
}
|
|
}
|
|
|
|
var _ routing.ChannelGraphSource = (*mockGraphSource)(nil)
|
|
|
|
func (r *mockGraphSource) AddNode(node *channeldb.LightningNode) error {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
r.nodes = append(r.nodes, *node)
|
|
return nil
|
|
}
|
|
|
|
func (r *mockGraphSource) AddEdge(info *channeldb.ChannelEdgeInfo) error {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
if _, ok := r.infos[info.ChannelID]; ok {
|
|
return errors.New("info already exist")
|
|
}
|
|
|
|
// Usually, the capacity is fetched in the router from the funding txout.
|
|
// Since the mockGraphSource can't access the txout, assign a default value.
|
|
info.Capacity = maxBtcFundingAmount
|
|
r.infos[info.ChannelID] = *info
|
|
return nil
|
|
}
|
|
|
|
func (r *mockGraphSource) UpdateEdge(edge *channeldb.ChannelEdgePolicy) error {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
r.edges[edge.ChannelID] = append(r.edges[edge.ChannelID], *edge)
|
|
return nil
|
|
}
|
|
|
|
func (r *mockGraphSource) SelfEdges() ([]*channeldb.ChannelEdgePolicy, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (r *mockGraphSource) CurrentBlockHeight() (uint32, error) {
|
|
return r.bestHeight, nil
|
|
}
|
|
|
|
func (r *mockGraphSource) AddProof(chanID lnwire.ShortChannelID,
|
|
proof *channeldb.ChannelAuthProof) error {
|
|
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
chanIDInt := chanID.ToUint64()
|
|
info, ok := r.infos[chanIDInt]
|
|
if !ok {
|
|
return errors.New("channel does not exist")
|
|
}
|
|
|
|
info.AuthProof = proof
|
|
r.infos[chanIDInt] = info
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *mockGraphSource) ForEachNode(func(node *channeldb.LightningNode) error) error {
|
|
return nil
|
|
}
|
|
|
|
func (r *mockGraphSource) ForAllOutgoingChannels(cb func(i *channeldb.ChannelEdgeInfo,
|
|
c *channeldb.ChannelEdgePolicy) error) error {
|
|
return nil
|
|
}
|
|
|
|
func (r *mockGraphSource) ForEachChannel(func(chanInfo *channeldb.ChannelEdgeInfo,
|
|
e1, e2 *channeldb.ChannelEdgePolicy) error) error {
|
|
return nil
|
|
}
|
|
|
|
func (r *mockGraphSource) GetChannelByID(chanID lnwire.ShortChannelID) (
|
|
*channeldb.ChannelEdgeInfo,
|
|
*channeldb.ChannelEdgePolicy,
|
|
*channeldb.ChannelEdgePolicy, error) {
|
|
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
chanInfo, ok := r.infos[chanID.ToUint64()]
|
|
if !ok {
|
|
return nil, nil, nil, channeldb.ErrEdgeNotFound
|
|
}
|
|
|
|
edges := r.edges[chanID.ToUint64()]
|
|
if len(edges) == 0 {
|
|
return &chanInfo, nil, nil, nil
|
|
}
|
|
|
|
if len(edges) == 1 {
|
|
edge1 := edges[0]
|
|
return &chanInfo, &edge1, nil, nil
|
|
}
|
|
|
|
edge1, edge2 := edges[0], edges[1]
|
|
return &chanInfo, &edge1, &edge2, nil
|
|
}
|
|
|
|
func (r *mockGraphSource) FetchLightningNode(
|
|
nodePub routing.Vertex) (*channeldb.LightningNode, error) {
|
|
|
|
for _, node := range r.nodes {
|
|
if bytes.Equal(nodePub[:], node.PubKeyBytes[:]) {
|
|
return &node, nil
|
|
}
|
|
}
|
|
|
|
return nil, channeldb.ErrGraphNodeNotFound
|
|
}
|
|
|
|
// IsStaleNode returns true if the graph source has a node announcement for the
|
|
// target node with a more recent timestamp.
|
|
func (r *mockGraphSource) IsStaleNode(nodePub routing.Vertex, timestamp time.Time) bool {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
for _, node := range r.nodes {
|
|
if node.PubKeyBytes == nodePub {
|
|
return node.LastUpdate.After(timestamp) ||
|
|
node.LastUpdate.Equal(timestamp)
|
|
}
|
|
}
|
|
|
|
// If we did not find the node among our existing graph nodes, we
|
|
// require the node to already have a channel in the graph to not be
|
|
// considered stale.
|
|
for _, info := range r.infos {
|
|
if info.NodeKey1Bytes == nodePub {
|
|
return false
|
|
}
|
|
if info.NodeKey2Bytes == nodePub {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// IsPublicNode determines whether the given vertex is seen as a public node in
|
|
// the graph from the graph's source node's point of view.
|
|
func (r *mockGraphSource) IsPublicNode(node routing.Vertex) (bool, error) {
|
|
for _, info := range r.infos {
|
|
if !bytes.Equal(node[:], info.NodeKey1Bytes[:]) &&
|
|
!bytes.Equal(node[:], info.NodeKey2Bytes[:]) {
|
|
continue
|
|
}
|
|
|
|
if info.AuthProof != nil {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
// IsKnownEdge returns true if the graph source already knows of the passed
|
|
// channel ID.
|
|
func (r *mockGraphSource) IsKnownEdge(chanID lnwire.ShortChannelID) bool {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
_, ok := r.infos[chanID.ToUint64()]
|
|
return ok
|
|
}
|
|
|
|
// IsStaleEdgePolicy returns true if the graph source has a channel edge for
|
|
// the passed channel ID (and flags) that have a more recent timestamp.
|
|
func (r *mockGraphSource) IsStaleEdgePolicy(chanID lnwire.ShortChannelID,
|
|
timestamp time.Time, flags lnwire.ChanUpdateChanFlags) bool {
|
|
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
edges, ok := r.edges[chanID.ToUint64()]
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
switch {
|
|
|
|
case len(edges) >= 1 && edges[0].ChannelFlags == flags:
|
|
return !edges[0].LastUpdate.Before(timestamp)
|
|
|
|
case len(edges) >= 2 && edges[1].ChannelFlags == flags:
|
|
return !edges[1].LastUpdate.Before(timestamp)
|
|
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
type mockNotifier struct {
|
|
clientCounter uint32
|
|
epochClients map[uint32]chan *chainntnfs.BlockEpoch
|
|
|
|
sync.RWMutex
|
|
}
|
|
|
|
func newMockNotifier() *mockNotifier {
|
|
return &mockNotifier{
|
|
epochClients: make(map[uint32]chan *chainntnfs.BlockEpoch),
|
|
}
|
|
}
|
|
|
|
func (m *mockNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
|
|
_ []byte, numConfs, _ uint32) (*chainntnfs.ConfirmationEvent, error) {
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *mockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, _ []byte,
|
|
_ uint32) (*chainntnfs.SpendEvent, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *mockNotifier) notifyBlock(hash chainhash.Hash, height uint32) {
|
|
m.RLock()
|
|
defer m.RUnlock()
|
|
|
|
for _, client := range m.epochClients {
|
|
client <- &chainntnfs.BlockEpoch{
|
|
Height: int32(height),
|
|
Hash: &hash,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *mockNotifier) RegisterBlockEpochNtfn(
|
|
bestBlock *chainntnfs.BlockEpoch) (*chainntnfs.BlockEpochEvent, error) {
|
|
m.RLock()
|
|
defer m.RUnlock()
|
|
|
|
epochChan := make(chan *chainntnfs.BlockEpoch)
|
|
clientID := m.clientCounter
|
|
m.clientCounter++
|
|
m.epochClients[clientID] = epochChan
|
|
|
|
return &chainntnfs.BlockEpochEvent{
|
|
Epochs: epochChan,
|
|
Cancel: func() {},
|
|
}, nil
|
|
}
|
|
|
|
func (m *mockNotifier) Start() error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockNotifier) Stop() error {
|
|
return nil
|
|
}
|
|
|
|
type annBatch struct {
|
|
nodeAnn1 *lnwire.NodeAnnouncement
|
|
nodeAnn2 *lnwire.NodeAnnouncement
|
|
|
|
localChanAnn *lnwire.ChannelAnnouncement
|
|
remoteChanAnn *lnwire.ChannelAnnouncement
|
|
|
|
chanUpdAnn1 *lnwire.ChannelUpdate
|
|
chanUpdAnn2 *lnwire.ChannelUpdate
|
|
|
|
localProofAnn *lnwire.AnnounceSignatures
|
|
remoteProofAnn *lnwire.AnnounceSignatures
|
|
}
|
|
|
|
func createAnnouncements(blockHeight uint32) (*annBatch, error) {
|
|
var err error
|
|
var batch annBatch
|
|
timestamp := uint32(123456)
|
|
|
|
batch.nodeAnn1, err = createNodeAnnouncement(nodeKeyPriv1, timestamp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
batch.nodeAnn2, err = createNodeAnnouncement(nodeKeyPriv2, timestamp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
batch.remoteChanAnn, err = createRemoteChannelAnnouncement(blockHeight)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
batch.localProofAnn = &lnwire.AnnounceSignatures{
|
|
NodeSignature: batch.remoteChanAnn.NodeSig1,
|
|
BitcoinSignature: batch.remoteChanAnn.BitcoinSig1,
|
|
}
|
|
|
|
batch.remoteProofAnn = &lnwire.AnnounceSignatures{
|
|
NodeSignature: batch.remoteChanAnn.NodeSig2,
|
|
BitcoinSignature: batch.remoteChanAnn.BitcoinSig2,
|
|
}
|
|
|
|
batch.localChanAnn, err = createRemoteChannelAnnouncement(blockHeight)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
batch.chanUpdAnn1, err = createUpdateAnnouncement(
|
|
blockHeight, 0, nodeKeyPriv1, timestamp,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
batch.chanUpdAnn2, err = createUpdateAnnouncement(
|
|
blockHeight, 1, nodeKeyPriv2, timestamp,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &batch, nil
|
|
|
|
}
|
|
|
|
func createNodeAnnouncement(priv *btcec.PrivateKey,
|
|
timestamp uint32, extraBytes ...[]byte) (*lnwire.NodeAnnouncement, error) {
|
|
|
|
var err error
|
|
k := hex.EncodeToString(priv.Serialize())
|
|
alias, err := lnwire.NewNodeAlias("kek" + k[:10])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
a := &lnwire.NodeAnnouncement{
|
|
Timestamp: timestamp,
|
|
Addresses: testAddrs,
|
|
Alias: alias,
|
|
Features: testFeatures,
|
|
}
|
|
copy(a.NodeID[:], priv.PubKey().SerializeCompressed())
|
|
if len(extraBytes) == 1 {
|
|
a.ExtraOpaqueData = extraBytes[0]
|
|
}
|
|
|
|
signer := mockSigner{priv}
|
|
sig, err := SignAnnouncement(&signer, priv.PubKey(), a)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
a.Signature, err = lnwire.NewSigFromSignature(sig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return a, nil
|
|
}
|
|
|
|
func createUpdateAnnouncement(blockHeight uint32,
|
|
flags lnwire.ChanUpdateChanFlags,
|
|
nodeKey *btcec.PrivateKey, timestamp uint32,
|
|
extraBytes ...[]byte) (*lnwire.ChannelUpdate, error) {
|
|
|
|
var err error
|
|
|
|
htlcMinMsat := lnwire.MilliSatoshi(prand.Int63())
|
|
a := &lnwire.ChannelUpdate{
|
|
ShortChannelID: lnwire.ShortChannelID{
|
|
BlockHeight: blockHeight,
|
|
},
|
|
Timestamp: timestamp,
|
|
MessageFlags: lnwire.ChanUpdateOptionMaxHtlc,
|
|
ChannelFlags: flags,
|
|
TimeLockDelta: uint16(prand.Int63()),
|
|
HtlcMinimumMsat: htlcMinMsat,
|
|
|
|
// Since the max HTLC must be greater than the min HTLC to pass channel
|
|
// update validation, set it to double the min htlc.
|
|
HtlcMaximumMsat: 2 * htlcMinMsat,
|
|
FeeRate: uint32(prand.Int31()),
|
|
BaseFee: uint32(prand.Int31()),
|
|
}
|
|
if len(extraBytes) == 1 {
|
|
a.ExtraOpaqueData = extraBytes[0]
|
|
}
|
|
|
|
err = signUpdate(nodeKey, a)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return a, nil
|
|
}
|
|
|
|
func signUpdate(nodeKey *btcec.PrivateKey, a *lnwire.ChannelUpdate) error {
|
|
pub := nodeKey.PubKey()
|
|
signer := mockSigner{nodeKey}
|
|
sig, err := SignAnnouncement(&signer, pub, a)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
a.Signature, err = lnwire.NewSigFromSignature(sig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func createAnnouncementWithoutProof(blockHeight uint32,
|
|
extraBytes ...[]byte) *lnwire.ChannelAnnouncement {
|
|
|
|
a := &lnwire.ChannelAnnouncement{
|
|
ShortChannelID: lnwire.ShortChannelID{
|
|
BlockHeight: blockHeight,
|
|
TxIndex: 0,
|
|
TxPosition: 0,
|
|
},
|
|
Features: testFeatures,
|
|
}
|
|
copy(a.NodeID1[:], nodeKeyPub1.SerializeCompressed())
|
|
copy(a.NodeID2[:], nodeKeyPub2.SerializeCompressed())
|
|
copy(a.BitcoinKey1[:], bitcoinKeyPub1.SerializeCompressed())
|
|
copy(a.BitcoinKey2[:], bitcoinKeyPub2.SerializeCompressed())
|
|
if len(extraBytes) == 1 {
|
|
a.ExtraOpaqueData = extraBytes[0]
|
|
}
|
|
|
|
return a
|
|
}
|
|
|
|
func createRemoteChannelAnnouncement(blockHeight uint32,
|
|
extraBytes ...[]byte) (*lnwire.ChannelAnnouncement, error) {
|
|
|
|
a := createAnnouncementWithoutProof(blockHeight, extraBytes...)
|
|
|
|
pub := nodeKeyPriv1.PubKey()
|
|
signer := mockSigner{nodeKeyPriv1}
|
|
sig, err := SignAnnouncement(&signer, pub, a)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
a.NodeSig1, err = lnwire.NewSigFromSignature(sig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pub = nodeKeyPriv2.PubKey()
|
|
signer = mockSigner{nodeKeyPriv2}
|
|
sig, err = SignAnnouncement(&signer, pub, a)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
a.NodeSig2, err = lnwire.NewSigFromSignature(sig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pub = bitcoinKeyPriv1.PubKey()
|
|
signer = mockSigner{bitcoinKeyPriv1}
|
|
sig, err = SignAnnouncement(&signer, pub, a)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
a.BitcoinSig1, err = lnwire.NewSigFromSignature(sig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pub = bitcoinKeyPriv2.PubKey()
|
|
signer = mockSigner{bitcoinKeyPriv2}
|
|
sig, err = SignAnnouncement(&signer, pub, a)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
a.BitcoinSig2, err = lnwire.NewSigFromSignature(sig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return a, nil
|
|
}
|
|
|
|
type testCtx struct {
|
|
gossiper *AuthenticatedGossiper
|
|
router *mockGraphSource
|
|
notifier *mockNotifier
|
|
broadcastedMessage chan msgWithSenders
|
|
}
|
|
|
|
func createTestCtx(startHeight uint32) (*testCtx, func(), error) {
|
|
// 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
|
|
// any p2p functionality, the peer send and switch send,
|
|
// broadcast functions won't be populated.
|
|
notifier := newMockNotifier()
|
|
router := newMockRouter(startHeight)
|
|
|
|
db, cleanUpDb, err := makeTestDB()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
waitingProofStore, err := channeldb.NewWaitingProofStore(db)
|
|
if err != nil {
|
|
cleanUpDb()
|
|
return nil, nil, err
|
|
}
|
|
|
|
broadcastedMessage := make(chan msgWithSenders, 10)
|
|
gossiper := New(Config{
|
|
Notifier: notifier,
|
|
Broadcast: func(senders map[routing.Vertex]struct{},
|
|
msgs ...lnwire.Message) error {
|
|
|
|
for _, msg := range msgs {
|
|
broadcastedMessage <- msgWithSenders{
|
|
msg: msg,
|
|
senders: senders,
|
|
}
|
|
}
|
|
|
|
return nil
|
|
},
|
|
FindPeer: func(target *btcec.PublicKey) (lnpeer.Peer, error) {
|
|
return &mockPeer{target, nil, nil}, nil
|
|
},
|
|
NotifyWhenOnline: func(target *btcec.PublicKey,
|
|
peerChan chan<- lnpeer.Peer) {
|
|
peerChan <- &mockPeer{target, nil, nil}
|
|
},
|
|
NotifyWhenOffline: func(_ [33]byte) <-chan struct{} {
|
|
c := make(chan struct{})
|
|
return c
|
|
},
|
|
Router: router,
|
|
TrickleDelay: trickleDelay,
|
|
RetransmitDelay: retransmitDelay,
|
|
ProofMatureDelta: proofMatureDelta,
|
|
WaitingProofStore: waitingProofStore,
|
|
MessageStore: newMockMessageStore(),
|
|
}, nodeKeyPub1)
|
|
|
|
if err := gossiper.Start(); err != nil {
|
|
cleanUpDb()
|
|
return nil, nil, fmt.Errorf("unable to start router: %v", err)
|
|
}
|
|
|
|
cleanUp := func() {
|
|
gossiper.Stop()
|
|
cleanUpDb()
|
|
}
|
|
|
|
return &testCtx{
|
|
router: router,
|
|
notifier: notifier,
|
|
gossiper: gossiper,
|
|
broadcastedMessage: broadcastedMessage,
|
|
}, cleanUp, nil
|
|
}
|
|
|
|
// TestProcessAnnouncement checks that mature announcements are propagated to
|
|
// the router subsystem.
|
|
func TestProcessAnnouncement(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
timestamp := uint32(123456)
|
|
|
|
ctx, cleanup, err := createTestCtx(0)
|
|
if err != nil {
|
|
t.Fatalf("can't create context: %v", err)
|
|
}
|
|
defer cleanup()
|
|
|
|
assertSenderExistence := func(sender *btcec.PublicKey, msg msgWithSenders) {
|
|
if _, ok := msg.senders[routing.NewVertex(sender)]; !ok {
|
|
t.Fatalf("sender=%x not present in %v",
|
|
sender.SerializeCompressed(), spew.Sdump(msg))
|
|
}
|
|
}
|
|
|
|
nodePeer := &mockPeer{nodeKeyPriv1.PubKey(), nil, nil}
|
|
|
|
// First, we'll craft a valid remote channel announcement and send it to
|
|
// the gossiper so that it can be processed.
|
|
ca, err := createRemoteChannelAnnouncement(0)
|
|
if err != nil {
|
|
t.Fatalf("can't create channel announcement: %v", err)
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(ca, nodePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("remote announcement not processed")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("can't process remote announcement: %v", err)
|
|
}
|
|
|
|
// The announcement should be broadcast and included in our local view
|
|
// of the graph.
|
|
select {
|
|
case msg := <-ctx.broadcastedMessage:
|
|
assertSenderExistence(nodePeer.IdentityKey(), msg)
|
|
case <-time.After(2 * trickleDelay):
|
|
t.Fatal("announcement wasn't proceeded")
|
|
}
|
|
|
|
if len(ctx.router.infos) != 1 {
|
|
t.Fatalf("edge wasn't added to router: %v", err)
|
|
}
|
|
|
|
// We'll then craft the channel policy of the remote party and also send
|
|
// it to the gossiper.
|
|
ua, err := createUpdateAnnouncement(0, 0, nodeKeyPriv1, timestamp)
|
|
if err != nil {
|
|
t.Fatalf("can't create update announcement: %v", err)
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(ua, nodePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("remote announcement not processed")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("can't process remote announcement: %v", err)
|
|
}
|
|
|
|
// The channel policy should be broadcast to the rest of the network.
|
|
select {
|
|
case msg := <-ctx.broadcastedMessage:
|
|
assertSenderExistence(nodePeer.IdentityKey(), msg)
|
|
case <-time.After(2 * trickleDelay):
|
|
t.Fatal("announcement wasn't proceeded")
|
|
}
|
|
|
|
if len(ctx.router.edges) != 1 {
|
|
t.Fatalf("edge update wasn't added to router: %v", err)
|
|
}
|
|
|
|
// Finally, we'll craft the remote party's node announcement.
|
|
na, err := createNodeAnnouncement(nodeKeyPriv1, timestamp)
|
|
if err != nil {
|
|
t.Fatalf("can't create node announcement: %v", err)
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(na, nodePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("remote announcement not processed")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("can't process remote announcement: %v", err)
|
|
}
|
|
|
|
// It should also be broadcast to the network and included in our local
|
|
// view of the graph.
|
|
select {
|
|
case msg := <-ctx.broadcastedMessage:
|
|
assertSenderExistence(nodePeer.IdentityKey(), msg)
|
|
case <-time.After(2 * trickleDelay):
|
|
t.Fatal("announcement wasn't proceeded")
|
|
}
|
|
|
|
if len(ctx.router.nodes) != 1 {
|
|
t.Fatalf("node wasn't added to router: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestPrematureAnnouncement checks that premature announcements are
|
|
// not propagated to the router subsystem until block with according
|
|
// block height received.
|
|
func TestPrematureAnnouncement(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
timestamp := uint32(123456)
|
|
|
|
ctx, cleanup, err := createTestCtx(0)
|
|
if err != nil {
|
|
t.Fatalf("can't create context: %v", err)
|
|
}
|
|
defer cleanup()
|
|
|
|
_, err = createNodeAnnouncement(nodeKeyPriv1, timestamp)
|
|
if err != nil {
|
|
t.Fatalf("can't create node announcement: %v", err)
|
|
}
|
|
|
|
nodePeer := &mockPeer{nodeKeyPriv1.PubKey(), nil, nil}
|
|
|
|
// Pretending that we receive the valid channel announcement from
|
|
// remote side, but block height of this announcement is greater than
|
|
// highest know to us, for that reason it should be added to the
|
|
// repeat/premature batch.
|
|
ca, err := createRemoteChannelAnnouncement(1)
|
|
if err != nil {
|
|
t.Fatalf("can't create channel announcement: %v", err)
|
|
}
|
|
|
|
select {
|
|
case <-ctx.gossiper.ProcessRemoteAnnouncement(ca, nodePeer):
|
|
t.Fatal("announcement was proceeded")
|
|
case <-time.After(100 * time.Millisecond):
|
|
}
|
|
|
|
if len(ctx.router.infos) != 0 {
|
|
t.Fatal("edge was added to router")
|
|
}
|
|
|
|
// Pretending that we receive the valid channel update announcement from
|
|
// remote side, but block height of this announcement is greater than
|
|
// highest know to us, for that reason it should be added to the
|
|
// repeat/premature batch.
|
|
ua, err := createUpdateAnnouncement(1, 0, nodeKeyPriv1, timestamp)
|
|
if err != nil {
|
|
t.Fatalf("can't create update announcement: %v", err)
|
|
}
|
|
|
|
select {
|
|
case <-ctx.gossiper.ProcessRemoteAnnouncement(ua, nodePeer):
|
|
t.Fatal("announcement was proceeded")
|
|
case <-time.After(100 * time.Millisecond):
|
|
}
|
|
|
|
if len(ctx.router.edges) != 0 {
|
|
t.Fatal("edge update was added to router")
|
|
}
|
|
|
|
// Generate new block and waiting the previously added announcements
|
|
// to be proceeded.
|
|
newBlock := &wire.MsgBlock{}
|
|
ctx.notifier.notifyBlock(newBlock.Header.BlockHash(), 1)
|
|
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
case <-time.After(2 * trickleDelay):
|
|
t.Fatal("announcement wasn't broadcasted")
|
|
}
|
|
|
|
if len(ctx.router.infos) != 1 {
|
|
t.Fatalf("edge wasn't added to router: %v", err)
|
|
}
|
|
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
case <-time.After(2 * trickleDelay):
|
|
t.Fatal("announcement wasn't broadcasted")
|
|
}
|
|
|
|
if len(ctx.router.edges) != 1 {
|
|
t.Fatalf("edge update wasn't added to router: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestSignatureAnnouncementLocalFirst ensures that the AuthenticatedGossiper
|
|
// properly processes partial and fully announcement signatures message.
|
|
func TestSignatureAnnouncementLocalFirst(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, cleanup, err := createTestCtx(uint32(proofMatureDelta))
|
|
if err != nil {
|
|
t.Fatalf("can't create context: %v", err)
|
|
}
|
|
defer cleanup()
|
|
|
|
// Set up a channel that we can use to inspect the messages sent
|
|
// directly from the gossiper.
|
|
sentMsgs := make(chan lnwire.Message, 10)
|
|
ctx.gossiper.cfg.FindPeer = func(target *btcec.PublicKey) (lnpeer.Peer, error) {
|
|
return &mockPeer{target, sentMsgs, ctx.gossiper.quit}, nil
|
|
}
|
|
ctx.gossiper.reliableSender.cfg.NotifyWhenOnline = func(target *btcec.PublicKey,
|
|
peerChan chan<- lnpeer.Peer) {
|
|
|
|
select {
|
|
case peerChan <- &mockPeer{target, sentMsgs, ctx.gossiper.quit}:
|
|
case <-ctx.gossiper.quit:
|
|
}
|
|
}
|
|
|
|
batch, err := createAnnouncements(0)
|
|
if err != nil {
|
|
t.Fatalf("can't generate announcements: %v", err)
|
|
}
|
|
|
|
localKey, err := btcec.ParsePubKey(batch.nodeAnn1.NodeID[:], btcec.S256())
|
|
if err != nil {
|
|
t.Fatalf("unable to parse pubkey: %v", err)
|
|
}
|
|
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:], btcec.S256())
|
|
if err != nil {
|
|
t.Fatalf("unable to parse pubkey: %v", err)
|
|
}
|
|
remotePeer := &mockPeer{remoteKey, sentMsgs, ctx.gossiper.quit}
|
|
|
|
// Recreate lightning network topology. Initialize router with channel
|
|
// between two nodes.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(
|
|
batch.localChanAnn, localKey,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process local announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process channel ann: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("channel announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(
|
|
batch.chanUpdAnn1, localKey,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process local announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process channel update: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("channel update announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(
|
|
batch.nodeAnn1, localKey,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process local announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process node ann: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("node announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
// The local ChannelUpdate should now be sent directly to the remote peer,
|
|
// such that the edge can be used for routing, regardless if this channel
|
|
// is announced or not (private channel).
|
|
select {
|
|
case msg := <-sentMsgs:
|
|
if msg != batch.chanUpdAnn1 {
|
|
t.Fatalf("expected local channel update, instead got %v", msg)
|
|
}
|
|
case <-time.After(1 * time.Second):
|
|
t.Fatal("gossiper did not send channel update to peer")
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(
|
|
batch.chanUpdAnn2, remotePeer,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process channel update: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("channel update announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(
|
|
batch.nodeAnn2, remotePeer,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process node ann: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("node announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
// Pretending that we receive local channel announcement from funding
|
|
// manager, thereby kick off the announcement exchange process.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(
|
|
batch.localProofAnn, localKey,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process local proof: %v", err)
|
|
}
|
|
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("announcements were broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
number := 0
|
|
if err := ctx.gossiper.cfg.WaitingProofStore.ForAll(
|
|
func(*channeldb.WaitingProof) error {
|
|
number++
|
|
return nil
|
|
},
|
|
); err != nil {
|
|
t.Fatalf("unable to retrieve objects from store: %v", err)
|
|
}
|
|
|
|
if number != 1 {
|
|
t.Fatal("wrong number of objects in storage")
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(
|
|
batch.remoteProofAnn, remotePeer,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process remote proof: %v", err)
|
|
}
|
|
|
|
for i := 0; i < 5; i++ {
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("announcement wasn't broadcast")
|
|
}
|
|
}
|
|
|
|
number = 0
|
|
if err := ctx.gossiper.cfg.WaitingProofStore.ForAll(
|
|
func(*channeldb.WaitingProof) error {
|
|
number++
|
|
return nil
|
|
},
|
|
); err != nil && err != channeldb.ErrWaitingProofNotFound {
|
|
t.Fatalf("unable to retrieve objects from store: %v", err)
|
|
}
|
|
|
|
if number != 0 {
|
|
t.Fatal("waiting proof should be removed from storage")
|
|
}
|
|
}
|
|
|
|
// TestOrphanSignatureAnnouncement ensures that the gossiper properly
|
|
// processes announcement with unknown channel ids.
|
|
func TestOrphanSignatureAnnouncement(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, cleanup, err := createTestCtx(uint32(proofMatureDelta))
|
|
if err != nil {
|
|
t.Fatalf("can't create context: %v", err)
|
|
}
|
|
defer cleanup()
|
|
|
|
// Set up a channel that we can use to inspect the messages sent
|
|
// directly from the gossiper.
|
|
sentMsgs := make(chan lnwire.Message, 10)
|
|
ctx.gossiper.cfg.FindPeer = func(target *btcec.PublicKey) (lnpeer.Peer, error) {
|
|
return &mockPeer{target, sentMsgs, ctx.gossiper.quit}, nil
|
|
}
|
|
ctx.gossiper.reliableSender.cfg.NotifyWhenOnline = func(target *btcec.PublicKey,
|
|
peerChan chan<- lnpeer.Peer) {
|
|
|
|
select {
|
|
case peerChan <- &mockPeer{target, sentMsgs, ctx.gossiper.quit}:
|
|
case <-ctx.gossiper.quit:
|
|
}
|
|
}
|
|
|
|
batch, err := createAnnouncements(0)
|
|
if err != nil {
|
|
t.Fatalf("can't generate announcements: %v", err)
|
|
}
|
|
|
|
localKey, err := btcec.ParsePubKey(batch.nodeAnn1.NodeID[:], btcec.S256())
|
|
if err != nil {
|
|
t.Fatalf("unable to parse pubkey: %v", err)
|
|
}
|
|
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:], btcec.S256())
|
|
if err != nil {
|
|
t.Fatalf("unable to parse pubkey: %v", err)
|
|
}
|
|
remotePeer := &mockPeer{remoteKey, sentMsgs, ctx.gossiper.quit}
|
|
|
|
// Pretending that we receive local channel announcement from funding
|
|
// manager, thereby kick off the announcement exchange process, in
|
|
// this case the announcement should be added in the orphan batch
|
|
// because we haven't announce the channel yet.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(batch.remoteProofAnn,
|
|
remotePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to proceed announcement: %v", err)
|
|
}
|
|
|
|
number := 0
|
|
if err := ctx.gossiper.cfg.WaitingProofStore.ForAll(
|
|
func(*channeldb.WaitingProof) error {
|
|
number++
|
|
return nil
|
|
},
|
|
); err != nil {
|
|
t.Fatalf("unable to retrieve objects from store: %v", err)
|
|
}
|
|
|
|
if number != 1 {
|
|
t.Fatal("wrong number of objects in storage")
|
|
}
|
|
|
|
// Recreate lightning network topology. Initialize router with channel
|
|
// between two nodes.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(batch.localChanAnn,
|
|
localKey):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process local announcement")
|
|
}
|
|
|
|
if err != nil {
|
|
t.Fatalf("unable to process: %v", err)
|
|
}
|
|
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("channel announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(batch.chanUpdAnn1,
|
|
localKey):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process local announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process: %v", err)
|
|
}
|
|
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("channel update announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(
|
|
batch.nodeAnn1, localKey,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process local announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process node ann: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("node announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
// The local ChannelUpdate should now be sent directly to the remote peer,
|
|
// such that the edge can be used for routing, regardless if this channel
|
|
// is announced or not (private channel).
|
|
select {
|
|
case msg := <-sentMsgs:
|
|
if msg != batch.chanUpdAnn1 {
|
|
t.Fatalf("expected local channel update, instead got %v", msg)
|
|
}
|
|
case <-time.After(1 * time.Second):
|
|
t.Fatal("gossiper did not send channel update to peer")
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(batch.chanUpdAnn2,
|
|
remotePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process node ann: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("channel update announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(
|
|
batch.nodeAnn2, remotePeer,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("node announcement announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
// After that we process local announcement, and waiting to receive
|
|
// the channel announcement.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(batch.localProofAnn,
|
|
localKey):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process: %v", err)
|
|
}
|
|
|
|
// The local proof should be sent to the remote peer.
|
|
select {
|
|
case msg := <-sentMsgs:
|
|
assertMessage(t, batch.localProofAnn, msg)
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatalf("local proof was not sent to peer")
|
|
}
|
|
|
|
// And since both remote and local announcements are processed, we
|
|
// should be broadcasting the final channel announcements.
|
|
for i := 0; i < 5; i++ {
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("announcement wasn't broadcast")
|
|
}
|
|
}
|
|
|
|
number = 0
|
|
if err := ctx.gossiper.cfg.WaitingProofStore.ForAll(
|
|
func(p *channeldb.WaitingProof) error {
|
|
number++
|
|
return nil
|
|
},
|
|
); err != nil {
|
|
t.Fatalf("unable to retrieve objects from store: %v", err)
|
|
}
|
|
|
|
if number != 0 {
|
|
t.Fatalf("wrong number of objects in storage: %v", number)
|
|
}
|
|
}
|
|
|
|
// TestSignatureAnnouncementRetryAtStartup tests that if we restart the
|
|
// gossiper, it will retry sending the AnnounceSignatures to the peer if it did
|
|
// not succeed before shutting down, and the full channel proof is not yet
|
|
// assembled.
|
|
func TestSignatureAnnouncementRetryAtStartup(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, cleanup, err := createTestCtx(uint32(proofMatureDelta))
|
|
if err != nil {
|
|
t.Fatalf("can't create context: %v", err)
|
|
}
|
|
defer cleanup()
|
|
|
|
batch, err := createAnnouncements(0)
|
|
if err != nil {
|
|
t.Fatalf("can't generate announcements: %v", err)
|
|
}
|
|
|
|
localKey, err := btcec.ParsePubKey(batch.nodeAnn1.NodeID[:], btcec.S256())
|
|
if err != nil {
|
|
t.Fatalf("unable to parse pubkey: %v", err)
|
|
}
|
|
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:], btcec.S256())
|
|
if err != nil {
|
|
t.Fatalf("unable to parse pubkey: %v", err)
|
|
}
|
|
|
|
// Set up a channel to intercept the messages sent to the remote peer.
|
|
sentToPeer := make(chan lnwire.Message, 1)
|
|
remotePeer := &mockPeer{remoteKey, sentToPeer, ctx.gossiper.quit}
|
|
|
|
// Recreate lightning network topology. Initialize router with channel
|
|
// between two nodes.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(
|
|
batch.localChanAnn, localKey,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process local announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process channel ann: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("channel announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(
|
|
batch.chanUpdAnn1, localKey,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process local announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process channel update: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("channel update announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(
|
|
batch.nodeAnn1, localKey,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process local announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process node ann: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("node announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(
|
|
batch.chanUpdAnn2, remotePeer,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process channel update: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("channel update announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(
|
|
batch.nodeAnn2, remotePeer,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process local announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process node ann: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("node announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
// Since the reliable send to the remote peer of the local channel proof
|
|
// requires a notification when the peer comes online, we'll capture the
|
|
// channel through which it gets sent to control exactly when to
|
|
// dispatch it.
|
|
notifyPeers := make(chan chan<- lnpeer.Peer, 1)
|
|
ctx.gossiper.reliableSender.cfg.NotifyWhenOnline = func(peer *btcec.PublicKey,
|
|
connectedChan chan<- lnpeer.Peer) {
|
|
notifyPeers <- connectedChan
|
|
}
|
|
|
|
// Pretending that we receive local channel announcement from funding
|
|
// manager, thereby kick off the announcement exchange process.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(batch.localProofAnn,
|
|
localKey):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process :%v", err)
|
|
}
|
|
|
|
// The gossiper should register for a notification for when the peer is
|
|
// online.
|
|
select {
|
|
case <-notifyPeers:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatalf("gossiper did not ask to get notified when " +
|
|
"peer is online")
|
|
}
|
|
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("announcements were broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
number := 0
|
|
if err := ctx.gossiper.cfg.WaitingProofStore.ForAll(
|
|
func(*channeldb.WaitingProof) error {
|
|
number++
|
|
return nil
|
|
},
|
|
); err != nil {
|
|
t.Fatalf("unable to retrieve objects from store: %v", err)
|
|
}
|
|
|
|
if number != 1 {
|
|
t.Fatal("wrong number of objects in storage")
|
|
}
|
|
|
|
// Shut down gossiper, and restart. This should trigger a new attempt
|
|
// to send the message to the peer.
|
|
ctx.gossiper.Stop()
|
|
gossiper := New(Config{
|
|
Notifier: ctx.gossiper.cfg.Notifier,
|
|
Broadcast: ctx.gossiper.cfg.Broadcast,
|
|
NotifyWhenOnline: ctx.gossiper.reliableSender.cfg.NotifyWhenOnline,
|
|
NotifyWhenOffline: ctx.gossiper.reliableSender.cfg.NotifyWhenOffline,
|
|
Router: ctx.gossiper.cfg.Router,
|
|
TrickleDelay: trickleDelay,
|
|
RetransmitDelay: retransmitDelay,
|
|
ProofMatureDelta: proofMatureDelta,
|
|
WaitingProofStore: ctx.gossiper.cfg.WaitingProofStore,
|
|
MessageStore: ctx.gossiper.cfg.MessageStore,
|
|
}, ctx.gossiper.selfKey)
|
|
if err != nil {
|
|
t.Fatalf("unable to recreate gossiper: %v", err)
|
|
}
|
|
if err := gossiper.Start(); err != nil {
|
|
t.Fatalf("unable to start recreated gossiper: %v", err)
|
|
}
|
|
defer gossiper.Stop()
|
|
|
|
ctx.gossiper = gossiper
|
|
remotePeer.quit = ctx.gossiper.quit
|
|
|
|
// After starting up, the gossiper will see that it has a proof in the
|
|
// WaitingProofStore, and will retry sending its part to the remote.
|
|
// It should register for a notification for when the peer is online.
|
|
var peerChan chan<- lnpeer.Peer
|
|
select {
|
|
case peerChan = <-notifyPeers:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatalf("gossiper did not ask to get notified when " +
|
|
"peer is online")
|
|
}
|
|
|
|
// Notify that peer is now online. This should allow the proof to be
|
|
// sent.
|
|
peerChan <- remotePeer
|
|
|
|
select {
|
|
case msg := <-sentToPeer:
|
|
assertMessage(t, batch.localProofAnn, msg)
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatalf("gossiper did not send message when peer came online")
|
|
}
|
|
|
|
// Now exchanging the remote channel proof, the channel announcement
|
|
// broadcast should continue as normal.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(
|
|
batch.remoteProofAnn, remotePeer,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process :%v", err)
|
|
}
|
|
|
|
for i := 0; i < 5; i++ {
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("announcement wasn't broadcast")
|
|
}
|
|
}
|
|
|
|
number = 0
|
|
if err := ctx.gossiper.cfg.WaitingProofStore.ForAll(
|
|
func(*channeldb.WaitingProof) error {
|
|
number++
|
|
return nil
|
|
},
|
|
); err != nil && err != channeldb.ErrWaitingProofNotFound {
|
|
t.Fatalf("unable to retrieve objects from store: %v", err)
|
|
}
|
|
|
|
if number != 0 {
|
|
t.Fatal("waiting proof should be removed from storage")
|
|
}
|
|
}
|
|
|
|
// TestSignatureAnnouncementFullProofWhenRemoteProof tests that if a remote
|
|
// proof is received when we already have the full proof, the gossiper will send
|
|
// the full proof (ChannelAnnouncement) to the remote peer.
|
|
func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, cleanup, err := createTestCtx(uint32(proofMatureDelta))
|
|
if err != nil {
|
|
t.Fatalf("can't create context: %v", err)
|
|
}
|
|
defer cleanup()
|
|
|
|
batch, err := createAnnouncements(0)
|
|
if err != nil {
|
|
t.Fatalf("can't generate announcements: %v", err)
|
|
}
|
|
|
|
localKey, err := btcec.ParsePubKey(batch.nodeAnn1.NodeID[:], btcec.S256())
|
|
if err != nil {
|
|
t.Fatalf("unable to parse pubkey: %v", err)
|
|
}
|
|
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:], btcec.S256())
|
|
if err != nil {
|
|
t.Fatalf("unable to parse pubkey: %v", err)
|
|
}
|
|
|
|
// Set up a channel we can use to inspect messages sent by the
|
|
// gossiper to the remote peer.
|
|
sentToPeer := make(chan lnwire.Message, 1)
|
|
remotePeer := &mockPeer{remoteKey, sentToPeer, ctx.gossiper.quit}
|
|
|
|
// Override NotifyWhenOnline to return the remote peer which we expect
|
|
// meesages to be sent to.
|
|
ctx.gossiper.reliableSender.cfg.NotifyWhenOnline = func(peer *btcec.PublicKey,
|
|
peerChan chan<- lnpeer.Peer) {
|
|
|
|
peerChan <- remotePeer
|
|
}
|
|
|
|
// Recreate lightning network topology. Initialize router with channel
|
|
// between two nodes.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(
|
|
batch.localChanAnn, localKey,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process local announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process channel ann: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("channel announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(
|
|
batch.chanUpdAnn1, localKey,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process local announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process channel update: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("channel update announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(
|
|
batch.nodeAnn1, localKey,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process local announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process node ann:%v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("node announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(
|
|
batch.chanUpdAnn2, remotePeer,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process channel update: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("channel update announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(
|
|
batch.nodeAnn2, remotePeer,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process node ann: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("node announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
// Pretending that we receive local channel announcement from funding
|
|
// manager, thereby kick off the announcement exchange process.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessLocalAnnouncement(
|
|
batch.localProofAnn, localKey,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process local announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process local proof: %v", err)
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(
|
|
batch.remoteProofAnn, remotePeer,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process local announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process remote proof: %v", err)
|
|
}
|
|
|
|
// We expect the gossiper to send this message to the remote peer.
|
|
select {
|
|
case msg := <-sentToPeer:
|
|
assertMessage(t, batch.localProofAnn, msg)
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not send local proof to peer")
|
|
}
|
|
|
|
// All channel and node announcements should be broadcast.
|
|
for i := 0; i < 5; i++ {
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("announcement wasn't broadcast")
|
|
}
|
|
}
|
|
|
|
number := 0
|
|
if err := ctx.gossiper.cfg.WaitingProofStore.ForAll(
|
|
func(*channeldb.WaitingProof) error {
|
|
number++
|
|
return nil
|
|
},
|
|
); err != nil && err != channeldb.ErrWaitingProofNotFound {
|
|
t.Fatalf("unable to retrieve objects from store: %v", err)
|
|
}
|
|
|
|
if number != 0 {
|
|
t.Fatal("waiting proof should be removed from storage")
|
|
}
|
|
|
|
// Now give the gossiper the remote proof yet again. This should
|
|
// trigger a send of the full ChannelAnnouncement.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(
|
|
batch.remoteProofAnn, remotePeer,
|
|
):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process local announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process remote proof: %v", err)
|
|
}
|
|
|
|
// We expect the gossiper to send this message to the remote peer.
|
|
select {
|
|
case msg := <-sentToPeer:
|
|
_, ok := msg.(*lnwire.ChannelAnnouncement)
|
|
if !ok {
|
|
t.Fatalf("expected ChannelAnnouncement, instead got %T", msg)
|
|
}
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not send local proof to peer")
|
|
}
|
|
}
|
|
|
|
// TestDeDuplicatedAnnouncements ensures that the deDupedAnnouncements struct
|
|
// properly stores and delivers the set of de-duplicated announcements.
|
|
func TestDeDuplicatedAnnouncements(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
timestamp := uint32(123456)
|
|
announcements := deDupedAnnouncements{}
|
|
announcements.Reset()
|
|
|
|
// Ensure that after new deDupedAnnouncements struct is created and
|
|
// reset that storage of each announcement type is empty.
|
|
if len(announcements.channelAnnouncements) != 0 {
|
|
t.Fatal("channel announcements map not empty after reset")
|
|
}
|
|
if len(announcements.channelUpdates) != 0 {
|
|
t.Fatal("channel updates map not empty after reset")
|
|
}
|
|
if len(announcements.nodeAnnouncements) != 0 {
|
|
t.Fatal("node announcements map not empty after reset")
|
|
}
|
|
|
|
// Ensure that remote channel announcements are properly stored
|
|
// and de-duplicated.
|
|
ca, err := createRemoteChannelAnnouncement(0)
|
|
if err != nil {
|
|
t.Fatalf("can't create remote channel announcement: %v", err)
|
|
}
|
|
|
|
nodePeer := &mockPeer{bitcoinKeyPub2, nil, nil}
|
|
announcements.AddMsgs(networkMsg{
|
|
msg: ca,
|
|
peer: nodePeer,
|
|
source: nodePeer.IdentityKey(),
|
|
})
|
|
if len(announcements.channelAnnouncements) != 1 {
|
|
t.Fatal("new channel announcement not stored in batch")
|
|
}
|
|
|
|
// We'll create a second instance of the same announcement with the
|
|
// same channel ID. Adding this shouldn't cause an increase in the
|
|
// number of items as they should be de-duplicated.
|
|
ca2, err := createRemoteChannelAnnouncement(0)
|
|
if err != nil {
|
|
t.Fatalf("can't create remote channel announcement: %v", err)
|
|
}
|
|
announcements.AddMsgs(networkMsg{
|
|
msg: ca2,
|
|
peer: nodePeer,
|
|
source: nodePeer.IdentityKey(),
|
|
})
|
|
if len(announcements.channelAnnouncements) != 1 {
|
|
t.Fatal("channel announcement not replaced in batch")
|
|
}
|
|
|
|
// Next, we'll ensure that channel update announcements are properly
|
|
// stored and de-duplicated. We do this by creating two updates
|
|
// announcements with the same short ID and flag.
|
|
ua, err := createUpdateAnnouncement(0, 0, nodeKeyPriv1, timestamp)
|
|
if err != nil {
|
|
t.Fatalf("can't create update announcement: %v", err)
|
|
}
|
|
announcements.AddMsgs(networkMsg{
|
|
msg: ua,
|
|
peer: nodePeer,
|
|
source: nodePeer.IdentityKey(),
|
|
})
|
|
if len(announcements.channelUpdates) != 1 {
|
|
t.Fatal("new channel update not stored in batch")
|
|
}
|
|
|
|
// Adding the very same announcement shouldn't cause an increase in the
|
|
// number of ChannelUpdate announcements stored.
|
|
ua2, err := createUpdateAnnouncement(0, 0, nodeKeyPriv1, timestamp)
|
|
if err != nil {
|
|
t.Fatalf("can't create update announcement: %v", err)
|
|
}
|
|
announcements.AddMsgs(networkMsg{
|
|
msg: ua2,
|
|
peer: nodePeer,
|
|
source: nodePeer.IdentityKey(),
|
|
})
|
|
if len(announcements.channelUpdates) != 1 {
|
|
t.Fatal("channel update not replaced in batch")
|
|
}
|
|
|
|
// Adding an announcement with a later timestamp should replace the
|
|
// stored one.
|
|
ua3, err := createUpdateAnnouncement(0, 0, nodeKeyPriv1, timestamp+1)
|
|
if err != nil {
|
|
t.Fatalf("can't create update announcement: %v", err)
|
|
}
|
|
announcements.AddMsgs(networkMsg{
|
|
msg: ua3,
|
|
peer: nodePeer,
|
|
source: nodePeer.IdentityKey(),
|
|
})
|
|
if len(announcements.channelUpdates) != 1 {
|
|
t.Fatal("channel update not replaced in batch")
|
|
}
|
|
|
|
assertChannelUpdate := func(channelUpdate *lnwire.ChannelUpdate) {
|
|
channelKey := channelUpdateID{
|
|
ua3.ShortChannelID,
|
|
ua3.ChannelFlags,
|
|
}
|
|
|
|
mws, ok := announcements.channelUpdates[channelKey]
|
|
if !ok {
|
|
t.Fatal("channel update not in batch")
|
|
}
|
|
if mws.msg != channelUpdate {
|
|
t.Fatalf("expected channel update %v, got %v)",
|
|
channelUpdate, mws.msg)
|
|
}
|
|
}
|
|
|
|
// Check that ua3 is the currently stored channel update.
|
|
assertChannelUpdate(ua3)
|
|
|
|
// Adding a channel update with an earlier timestamp should NOT
|
|
// replace the one stored.
|
|
ua4, err := createUpdateAnnouncement(0, 0, nodeKeyPriv1, timestamp)
|
|
if err != nil {
|
|
t.Fatalf("can't create update announcement: %v", err)
|
|
}
|
|
announcements.AddMsgs(networkMsg{
|
|
msg: ua4,
|
|
peer: nodePeer,
|
|
source: nodePeer.IdentityKey(),
|
|
})
|
|
if len(announcements.channelUpdates) != 1 {
|
|
t.Fatal("channel update not in batch")
|
|
}
|
|
assertChannelUpdate(ua3)
|
|
|
|
// Next well ensure that node announcements are properly de-duplicated.
|
|
// We'll first add a single instance with a node's private key.
|
|
na, err := createNodeAnnouncement(nodeKeyPriv1, timestamp)
|
|
if err != nil {
|
|
t.Fatalf("can't create node announcement: %v", err)
|
|
}
|
|
announcements.AddMsgs(networkMsg{
|
|
msg: na,
|
|
peer: nodePeer,
|
|
source: nodePeer.IdentityKey(),
|
|
})
|
|
if len(announcements.nodeAnnouncements) != 1 {
|
|
t.Fatal("new node announcement not stored in batch")
|
|
}
|
|
|
|
// We'll now add another node to the batch.
|
|
na2, err := createNodeAnnouncement(nodeKeyPriv2, timestamp)
|
|
if err != nil {
|
|
t.Fatalf("can't create node announcement: %v", err)
|
|
}
|
|
announcements.AddMsgs(networkMsg{
|
|
msg: na2,
|
|
peer: nodePeer,
|
|
source: nodePeer.IdentityKey(),
|
|
})
|
|
if len(announcements.nodeAnnouncements) != 2 {
|
|
t.Fatal("second node announcement not stored in batch")
|
|
}
|
|
|
|
// Adding a new instance of the _same_ node shouldn't increase the size
|
|
// of the node ann batch.
|
|
na3, err := createNodeAnnouncement(nodeKeyPriv2, timestamp)
|
|
if err != nil {
|
|
t.Fatalf("can't create node announcement: %v", err)
|
|
}
|
|
announcements.AddMsgs(networkMsg{
|
|
msg: na3,
|
|
peer: nodePeer,
|
|
source: nodePeer.IdentityKey(),
|
|
})
|
|
if len(announcements.nodeAnnouncements) != 2 {
|
|
t.Fatal("second node announcement not replaced in batch")
|
|
}
|
|
|
|
// Ensure that node announcement with different pointer to same public
|
|
// key is still de-duplicated.
|
|
newNodeKeyPointer := nodeKeyPriv2
|
|
na4, err := createNodeAnnouncement(newNodeKeyPointer, timestamp)
|
|
if err != nil {
|
|
t.Fatalf("can't create node announcement: %v", err)
|
|
}
|
|
announcements.AddMsgs(networkMsg{
|
|
msg: na4,
|
|
peer: nodePeer,
|
|
source: nodePeer.IdentityKey(),
|
|
})
|
|
if len(announcements.nodeAnnouncements) != 2 {
|
|
t.Fatal("second node announcement not replaced again in batch")
|
|
}
|
|
|
|
// Ensure that node announcement with increased timestamp replaces
|
|
// what is currently stored.
|
|
na5, err := createNodeAnnouncement(nodeKeyPriv2, timestamp+1)
|
|
if err != nil {
|
|
t.Fatalf("can't create node announcement: %v", err)
|
|
}
|
|
announcements.AddMsgs(networkMsg{
|
|
msg: na5,
|
|
peer: nodePeer,
|
|
source: nodePeer.IdentityKey(),
|
|
})
|
|
if len(announcements.nodeAnnouncements) != 2 {
|
|
t.Fatal("node announcement not replaced in batch")
|
|
}
|
|
nodeID := routing.NewVertex(nodeKeyPriv2.PubKey())
|
|
stored, ok := announcements.nodeAnnouncements[nodeID]
|
|
if !ok {
|
|
t.Fatalf("node announcement not found in batch")
|
|
}
|
|
if stored.msg != na5 {
|
|
t.Fatalf("expected de-duped node announcement to be %v, got %v",
|
|
na5, stored.msg)
|
|
}
|
|
|
|
// Ensure that announcement batch delivers channel announcements,
|
|
// channel updates, and node announcements in proper order.
|
|
batch := announcements.Emit()
|
|
if len(batch) != 4 {
|
|
t.Fatal("announcement batch incorrect length")
|
|
}
|
|
|
|
if !reflect.DeepEqual(batch[0].msg, ca2) {
|
|
t.Fatalf("channel announcement not first in batch: got %v, "+
|
|
"expected %v", spew.Sdump(batch[0].msg), spew.Sdump(ca2))
|
|
}
|
|
|
|
if !reflect.DeepEqual(batch[1].msg, ua3) {
|
|
t.Fatalf("channel update not next in batch: got %v, "+
|
|
"expected %v", spew.Sdump(batch[1].msg), spew.Sdump(ua2))
|
|
}
|
|
|
|
// We'll ensure that both node announcements are present. We check both
|
|
// indexes as due to the randomized order of map iteration they may be
|
|
// in either place.
|
|
if !reflect.DeepEqual(batch[2].msg, na) && !reflect.DeepEqual(batch[3].msg, na) {
|
|
t.Fatal("first node announcement not in last part of batch: "+
|
|
"got %v, expected %v", batch[2].msg,
|
|
na)
|
|
}
|
|
if !reflect.DeepEqual(batch[2].msg, na5) && !reflect.DeepEqual(batch[3].msg, na5) {
|
|
t.Fatalf("second node announcement not in last part of batch: "+
|
|
"got %v, expected %v", batch[3].msg,
|
|
na5)
|
|
}
|
|
|
|
// Ensure that after reset, storage of each announcement type
|
|
// in deDupedAnnouncements struct is empty again.
|
|
announcements.Reset()
|
|
if len(announcements.channelAnnouncements) != 0 {
|
|
t.Fatal("channel announcements map not empty after reset")
|
|
}
|
|
if len(announcements.channelUpdates) != 0 {
|
|
t.Fatal("channel updates map not empty after reset")
|
|
}
|
|
if len(announcements.nodeAnnouncements) != 0 {
|
|
t.Fatal("node announcements map not empty after reset")
|
|
}
|
|
}
|
|
|
|
// TestForwardPrivateNodeAnnouncement ensures that we do not forward node
|
|
// announcements for nodes who do not intend to publicly advertise themselves.
|
|
func TestForwardPrivateNodeAnnouncement(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
const (
|
|
startingHeight = 100
|
|
timestamp = 123456
|
|
)
|
|
|
|
ctx, cleanup, err := createTestCtx(startingHeight)
|
|
if err != nil {
|
|
t.Fatalf("can't create context: %v", err)
|
|
}
|
|
defer cleanup()
|
|
|
|
// We'll start off by processing a channel announcement without a proof
|
|
// (i.e., an unadvertised channel), followed by a node announcement for
|
|
// this same channel announcement.
|
|
chanAnn := createAnnouncementWithoutProof(startingHeight - 2)
|
|
pubKey := nodeKeyPriv1.PubKey()
|
|
|
|
select {
|
|
case err := <-ctx.gossiper.ProcessLocalAnnouncement(chanAnn, pubKey):
|
|
if err != nil {
|
|
t.Fatalf("unable to process local announcement: %v", err)
|
|
}
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatalf("local announcement not processed")
|
|
}
|
|
|
|
// The gossiper should not broadcast the announcement due to it not
|
|
// having its announcement signatures.
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("gossiper should not have broadcast channel announcement")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
nodeAnn, err := createNodeAnnouncement(nodeKeyPriv1, timestamp)
|
|
if err != nil {
|
|
t.Fatalf("unable to create node announcement: %v", err)
|
|
}
|
|
|
|
select {
|
|
case err := <-ctx.gossiper.ProcessLocalAnnouncement(nodeAnn, pubKey):
|
|
if err != nil {
|
|
t.Fatalf("unable to process remote announcement: %v", err)
|
|
}
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("remote announcement not processed")
|
|
}
|
|
|
|
// The gossiper should also not broadcast the node announcement due to
|
|
// it not being part of any advertised channels.
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("gossiper should not have broadcast node announcement")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
// Now, we'll attempt to forward the NodeAnnouncement for the same node
|
|
// by opening a public channel on the network. We'll create a
|
|
// ChannelAnnouncement and hand it off to the gossiper in order to
|
|
// process it.
|
|
remoteChanAnn, err := createRemoteChannelAnnouncement(startingHeight - 1)
|
|
if err != nil {
|
|
t.Fatalf("unable to create remote channel announcement: %v", err)
|
|
}
|
|
peer := &mockPeer{pubKey, nil, nil}
|
|
|
|
select {
|
|
case err := <-ctx.gossiper.ProcessRemoteAnnouncement(remoteChanAnn, peer):
|
|
if err != nil {
|
|
t.Fatalf("unable to process remote announcement: %v", err)
|
|
}
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("remote announcement not processed")
|
|
}
|
|
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
case <-time.After(2 * trickleDelay):
|
|
t.Fatal("gossiper should have broadcast the channel announcement")
|
|
}
|
|
|
|
// We'll recreate the NodeAnnouncement with an updated timestamp to
|
|
// prevent a stale update. The NodeAnnouncement should now be forwarded.
|
|
nodeAnn, err = createNodeAnnouncement(nodeKeyPriv1, timestamp+1)
|
|
if err != nil {
|
|
t.Fatalf("unable to create node announcement: %v", err)
|
|
}
|
|
|
|
select {
|
|
case err := <-ctx.gossiper.ProcessRemoteAnnouncement(nodeAnn, peer):
|
|
if err != nil {
|
|
t.Fatalf("unable to process remote announcement: %v", err)
|
|
}
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("remote announcement not processed")
|
|
}
|
|
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
case <-time.After(2 * trickleDelay):
|
|
t.Fatal("gossiper should have broadcast the node announcement")
|
|
}
|
|
}
|
|
|
|
// TestReceiveRemoteChannelUpdateFirst tests that if we receive a ChannelUpdate
|
|
// from the remote before we have processed our own ChannelAnnouncement, it will
|
|
// be reprocessed later, after our ChannelAnnouncement.
|
|
func TestReceiveRemoteChannelUpdateFirst(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, cleanup, err := createTestCtx(uint32(proofMatureDelta))
|
|
if err != nil {
|
|
t.Fatalf("can't create context: %v", err)
|
|
}
|
|
defer cleanup()
|
|
|
|
batch, err := createAnnouncements(0)
|
|
if err != nil {
|
|
t.Fatalf("can't generate announcements: %v", err)
|
|
}
|
|
|
|
localKey, err := btcec.ParsePubKey(batch.nodeAnn1.NodeID[:], btcec.S256())
|
|
if err != nil {
|
|
t.Fatalf("unable to parse pubkey: %v", err)
|
|
}
|
|
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:], btcec.S256())
|
|
if err != nil {
|
|
t.Fatalf("unable to parse pubkey: %v", err)
|
|
}
|
|
|
|
// Set up a channel that we can use to inspect the messages sent
|
|
// directly from the gossiper.
|
|
sentMsgs := make(chan lnwire.Message, 10)
|
|
remotePeer := &mockPeer{remoteKey, sentMsgs, ctx.gossiper.quit}
|
|
|
|
// Override NotifyWhenOnline and FindPeer to return the remote peer
|
|
// which we expect meesages to be sent to.
|
|
ctx.gossiper.cfg.FindPeer = func(target *btcec.PublicKey) (lnpeer.Peer, error) {
|
|
return remotePeer, nil
|
|
}
|
|
ctx.gossiper.reliableSender.cfg.NotifyWhenOnline = func(peer *btcec.PublicKey,
|
|
peerChan chan<- lnpeer.Peer) {
|
|
|
|
peerChan <- remotePeer
|
|
}
|
|
|
|
// Recreate the case where the remote node is sending us its ChannelUpdate
|
|
// before we have been able to process our own ChannelAnnouncement and
|
|
// ChannelUpdate.
|
|
errRemoteAnn := ctx.gossiper.ProcessRemoteAnnouncement(
|
|
batch.chanUpdAnn2, remotePeer,
|
|
)
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("channel update announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
err = <-ctx.gossiper.ProcessRemoteAnnouncement(batch.nodeAnn2, remotePeer)
|
|
if err != nil {
|
|
t.Fatalf("unable to process node ann: %v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("node announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
// Since the remote ChannelUpdate was added for an edge that
|
|
// we did not already know about, it should have been added
|
|
// to the map of premature ChannelUpdates. Check that nothing
|
|
// was added to the graph.
|
|
chanInfo, e1, e2, err := ctx.router.GetChannelByID(batch.chanUpdAnn1.ShortChannelID)
|
|
if err != channeldb.ErrEdgeNotFound {
|
|
t.Fatalf("Expected ErrEdgeNotFound, got: %v", err)
|
|
}
|
|
if chanInfo != nil {
|
|
t.Fatalf("chanInfo was not nil")
|
|
}
|
|
if e1 != nil {
|
|
t.Fatalf("e1 was not nil")
|
|
}
|
|
if e2 != nil {
|
|
t.Fatalf("e2 was not nil")
|
|
}
|
|
|
|
// Recreate lightning network topology. Initialize router with channel
|
|
// between two nodes.
|
|
err = <-ctx.gossiper.ProcessLocalAnnouncement(batch.localChanAnn, localKey)
|
|
if err != nil {
|
|
t.Fatalf("unable to process :%v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("channel announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
err = <-ctx.gossiper.ProcessLocalAnnouncement(batch.chanUpdAnn1, localKey)
|
|
if err != nil {
|
|
t.Fatalf("unable to process :%v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("channel update announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
err = <-ctx.gossiper.ProcessLocalAnnouncement(batch.nodeAnn1, localKey)
|
|
if err != nil {
|
|
t.Fatalf("unable to process :%v", err)
|
|
}
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("node announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
// The local ChannelUpdate should now be sent directly to the remote peer,
|
|
// such that the edge can be used for routing, regardless if this channel
|
|
// is announced or not (private channel).
|
|
select {
|
|
case msg := <-sentMsgs:
|
|
if msg != batch.chanUpdAnn1 {
|
|
t.Fatalf("expected local channel update, instead got %v", msg)
|
|
}
|
|
case <-time.After(1 * time.Second):
|
|
t.Fatal("gossiper did not send channel update to peer")
|
|
}
|
|
|
|
// At this point the remote ChannelUpdate we received earlier should
|
|
// be reprocessed, as we now have the necessary edge entry in the graph.
|
|
select {
|
|
case err := <-errRemoteAnn:
|
|
if err != nil {
|
|
t.Fatalf("error re-processing remote update: %v", err)
|
|
}
|
|
case <-time.After(2 * trickleDelay):
|
|
t.Fatalf("remote update was not processed")
|
|
}
|
|
|
|
// Check that the ChannelEdgePolicy was added to the graph.
|
|
chanInfo, e1, e2, err = ctx.router.GetChannelByID(
|
|
batch.chanUpdAnn1.ShortChannelID,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("unable to get channel from router: %v", err)
|
|
}
|
|
if chanInfo == nil {
|
|
t.Fatalf("chanInfo was nil")
|
|
}
|
|
if e1 == nil {
|
|
t.Fatalf("e1 was nil")
|
|
}
|
|
if e2 == nil {
|
|
t.Fatalf("e2 was nil")
|
|
}
|
|
|
|
// Pretending that we receive local channel announcement from funding
|
|
// manager, thereby kick off the announcement exchange process.
|
|
err = <-ctx.gossiper.ProcessLocalAnnouncement(
|
|
batch.localProofAnn, localKey,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("unable to process :%v", err)
|
|
}
|
|
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("announcements were broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
number := 0
|
|
if err := ctx.gossiper.cfg.WaitingProofStore.ForAll(
|
|
func(*channeldb.WaitingProof) error {
|
|
number++
|
|
return nil
|
|
},
|
|
); err != nil {
|
|
t.Fatalf("unable to retrieve objects from store: %v", err)
|
|
}
|
|
|
|
if number != 1 {
|
|
t.Fatal("wrong number of objects in storage")
|
|
}
|
|
|
|
err = <-ctx.gossiper.ProcessRemoteAnnouncement(
|
|
batch.remoteProofAnn, remotePeer,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("unable to process :%v", err)
|
|
}
|
|
|
|
for i := 0; i < 4; i++ {
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("announcement wasn't broadcast")
|
|
}
|
|
}
|
|
|
|
number = 0
|
|
if err := ctx.gossiper.cfg.WaitingProofStore.ForAll(
|
|
func(*channeldb.WaitingProof) error {
|
|
number++
|
|
return nil
|
|
},
|
|
); err != nil && err != channeldb.ErrWaitingProofNotFound {
|
|
t.Fatalf("unable to retrieve objects from store: %v", err)
|
|
}
|
|
|
|
if number != 0 {
|
|
t.Fatal("waiting proof should be removed from storage")
|
|
}
|
|
}
|
|
|
|
// TestExtraDataChannelAnnouncementValidation tests that we're able to properly
|
|
// validate a ChannelAnnouncement that includes opaque bytes that we don't
|
|
// currently know of.
|
|
func TestExtraDataChannelAnnouncementValidation(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, cleanup, err := createTestCtx(0)
|
|
if err != nil {
|
|
t.Fatalf("can't create context: %v", err)
|
|
}
|
|
defer cleanup()
|
|
|
|
remotePeer := &mockPeer{nodeKeyPriv1.PubKey(), nil, nil}
|
|
|
|
// We'll now create an announcement that contains an extra set of bytes
|
|
// that we don't know of ourselves, but should still include in the
|
|
// final signature check.
|
|
extraBytes := []byte("gotta validate this stil!")
|
|
ca, err := createRemoteChannelAnnouncement(0, extraBytes)
|
|
if err != nil {
|
|
t.Fatalf("can't create channel announcement: %v", err)
|
|
}
|
|
|
|
// We'll now send the announcement to the main gossiper. We should be
|
|
// able to validate this announcement to problem.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(ca, remotePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process :%v", err)
|
|
}
|
|
}
|
|
|
|
// TestExtraDataChannelUpdateValidation tests that we're able to properly
|
|
// validate a ChannelUpdate that includes opaque bytes that we don't currently
|
|
// know of.
|
|
func TestExtraDataChannelUpdateValidation(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, cleanup, err := createTestCtx(0)
|
|
if err != nil {
|
|
t.Fatalf("can't create context: %v", err)
|
|
}
|
|
defer cleanup()
|
|
|
|
remotePeer := &mockPeer{nodeKeyPriv1.PubKey(), nil, nil}
|
|
timestamp := uint32(123456)
|
|
|
|
// In this scenario, we'll create two announcements, one regular
|
|
// channel announcement, and another channel update announcement, that
|
|
// has additional data that we won't be interpreting.
|
|
chanAnn, err := createRemoteChannelAnnouncement(0)
|
|
if err != nil {
|
|
t.Fatalf("unable to create chan ann: %v", err)
|
|
}
|
|
chanUpdAnn1, err := createUpdateAnnouncement(
|
|
0, 0, nodeKeyPriv1, timestamp,
|
|
[]byte("must also validate"),
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("unable to create chan up: %v", err)
|
|
}
|
|
chanUpdAnn2, err := createUpdateAnnouncement(
|
|
0, 1, nodeKeyPriv2, timestamp,
|
|
[]byte("must also validate"),
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("unable to create chan up: %v", err)
|
|
}
|
|
|
|
// We should be able to properly validate all three messages without
|
|
// any issue.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(chanAnn, remotePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process announcement: %v", err)
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(chanUpdAnn1, remotePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process announcement: %v", err)
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(chanUpdAnn2, remotePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process announcement: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestExtraDataNodeAnnouncementValidation tests that we're able to properly
|
|
// validate a NodeAnnouncement that includes opaque bytes that we don't
|
|
// currently know of.
|
|
func TestExtraDataNodeAnnouncementValidation(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, cleanup, err := createTestCtx(0)
|
|
if err != nil {
|
|
t.Fatalf("can't create context: %v", err)
|
|
}
|
|
defer cleanup()
|
|
|
|
remotePeer := &mockPeer{nodeKeyPriv1.PubKey(), nil, nil}
|
|
timestamp := uint32(123456)
|
|
|
|
// We'll create a node announcement that includes a set of opaque data
|
|
// which we don't know of, but will store anyway in order to ensure
|
|
// upgrades can flow smoothly in the future.
|
|
nodeAnn, err := createNodeAnnouncement(
|
|
nodeKeyPriv1, timestamp, []byte("gotta validate"),
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("can't create node announcement: %v", err)
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(nodeAnn, remotePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process announcement: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestNodeAnnouncementNoChannels tests that NodeAnnouncements for nodes with
|
|
// no existing channels in the graph do not get forwarded.
|
|
func TestNodeAnnouncementNoChannels(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, cleanup, err := createTestCtx(0)
|
|
if err != nil {
|
|
t.Fatalf("can't create context: %v", err)
|
|
}
|
|
defer cleanup()
|
|
|
|
batch, err := createAnnouncements(0)
|
|
if err != nil {
|
|
t.Fatalf("can't generate announcements: %v", err)
|
|
}
|
|
|
|
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:],
|
|
btcec.S256())
|
|
if err != nil {
|
|
t.Fatalf("unable to parse pubkey: %v", err)
|
|
}
|
|
remotePeer := &mockPeer{remoteKey, nil, nil}
|
|
|
|
// Process the remote node announcement.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(batch.nodeAnn2,
|
|
remotePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process announcement: %v", err)
|
|
}
|
|
|
|
// Since no channels or node announcements were already in the graph,
|
|
// the node announcement should be ignored, and not forwarded.
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("node announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
|
|
// Now add the node's channel to the graph by processing the channel
|
|
// announement and channel update.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(batch.remoteChanAnn,
|
|
remotePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process announcement: %v", err)
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(batch.chanUpdAnn2,
|
|
remotePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process announcement: %v", err)
|
|
}
|
|
|
|
// Now process the node announcement again.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(batch.nodeAnn2, remotePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process announcement: %v", err)
|
|
}
|
|
|
|
// This time the node announcement should be forwarded. The same should
|
|
// the channel announcement and update be.
|
|
for i := 0; i < 3; i++ {
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("announcement wasn't broadcast")
|
|
}
|
|
}
|
|
|
|
// Processing the same node announement again should be ignored, as it
|
|
// is stale.
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(batch.nodeAnn2,
|
|
remotePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process announcement: %v", err)
|
|
}
|
|
|
|
select {
|
|
case <-ctx.broadcastedMessage:
|
|
t.Fatal("node announcement was broadcast")
|
|
case <-time.After(2 * trickleDelay):
|
|
}
|
|
}
|
|
|
|
// TestOptionalFieldsChannelUpdateValidation tests that we're able to properly
|
|
// validate the msg flags and optional max HTLC field of a ChannelUpdate.
|
|
func TestOptionalFieldsChannelUpdateValidation(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, cleanup, err := createTestCtx(0)
|
|
if err != nil {
|
|
t.Fatalf("can't create context: %v", err)
|
|
}
|
|
defer cleanup()
|
|
|
|
chanUpdateHeight := uint32(0)
|
|
timestamp := uint32(123456)
|
|
nodePeer := &mockPeer{nodeKeyPriv1.PubKey(), nil, nil}
|
|
|
|
// In this scenario, we'll test whether the message flags field in a channel
|
|
// update is properly handled.
|
|
chanAnn, err := createRemoteChannelAnnouncement(chanUpdateHeight)
|
|
if err != nil {
|
|
t.Fatalf("can't create channel announcement: %v", err)
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(chanAnn, nodePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process announcement: %v", err)
|
|
}
|
|
|
|
// The first update should fail from an invalid max HTLC field, which is
|
|
// less than the min HTLC.
|
|
chanUpdAnn, err := createUpdateAnnouncement(0, 0, nodeKeyPriv1, timestamp)
|
|
if err != nil {
|
|
t.Fatalf("unable to create channel update: %v", err)
|
|
}
|
|
|
|
chanUpdAnn.HtlcMinimumMsat = 5000
|
|
chanUpdAnn.HtlcMaximumMsat = 4000
|
|
if err := signUpdate(nodeKeyPriv1, chanUpdAnn); err != nil {
|
|
t.Fatalf("unable to sign channel update: %v", err)
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(chanUpdAnn, nodePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err == nil || !strings.Contains(err.Error(), "invalid max htlc") {
|
|
t.Fatalf("expected chan update to error, instead got %v", err)
|
|
}
|
|
|
|
// The second update should fail because the message flag is set but
|
|
// the max HTLC field is 0.
|
|
chanUpdAnn.HtlcMinimumMsat = 0
|
|
chanUpdAnn.HtlcMaximumMsat = 0
|
|
if err := signUpdate(nodeKeyPriv1, chanUpdAnn); err != nil {
|
|
t.Fatalf("unable to sign channel update: %v", err)
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(chanUpdAnn, nodePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err == nil || !strings.Contains(err.Error(), "invalid max htlc") {
|
|
t.Fatalf("expected chan update to error, instead got %v", err)
|
|
}
|
|
|
|
// The final update should succeed, since setting the flag 0 means the
|
|
// nonsense max_htlc field will just be ignored.
|
|
chanUpdAnn.MessageFlags = 0
|
|
if err := signUpdate(nodeKeyPriv1, chanUpdAnn); err != nil {
|
|
t.Fatalf("unable to sign channel update: %v", err)
|
|
}
|
|
|
|
select {
|
|
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(chanUpdAnn, nodePeer):
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("did not process remote announcement")
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("unable to process announcement: %v", err)
|
|
}
|
|
}
|
|
|
|
func assertMessage(t *testing.T, expected, got lnwire.Message) {
|
|
t.Helper()
|
|
|
|
if !reflect.DeepEqual(expected, got) {
|
|
t.Fatalf("expected: %v\ngot: %v", spew.Sdump(expected),
|
|
spew.Sdump(got))
|
|
}
|
|
}
|