github.com/decred/dcrlnd@v0.7.6/routing/notifications_test.go (about)

     1  package routing
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"image/color"
     7  	"net"
     8  	"sync"
     9  	"testing"
    10  	"time"
    11  
    12  	prand "math/rand"
    13  
    14  	"github.com/decred/dcrd/chaincfg/chainhash"
    15  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    16  	"github.com/decred/dcrd/wire"
    17  	"github.com/decred/dcrlnd/channeldb"
    18  	"github.com/stretchr/testify/require"
    19  
    20  	"github.com/decred/dcrd/dcrutil/v4"
    21  	"github.com/decred/dcrlnd/input"
    22  	"github.com/decred/dcrlnd/lnwallet"
    23  	"github.com/decred/dcrlnd/lnwire"
    24  	"github.com/decred/dcrlnd/routing/chainview"
    25  	"github.com/decred/dcrlnd/routing/route"
    26  	"github.com/go-errors/errors"
    27  )
    28  
    29  var (
    30  	testAddr = &net.TCPAddr{IP: (net.IP)([]byte{0xA, 0x0, 0x0, 0x1}),
    31  		Port: 9000}
    32  	testAddrs = []net.Addr{testAddr}
    33  
    34  	testFeatures = lnwire.NewFeatureVector(nil, lnwire.Features)
    35  
    36  	testHash = [32]byte{
    37  		0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab,
    38  		0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4,
    39  		0x4f, 0x2f, 0x6f, 0x25, 0x88, 0xa3, 0xef, 0xb9,
    40  		0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53,
    41  	}
    42  
    43  	testTime = time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
    44  
    45  	priv1, _    = secp256k1.GeneratePrivateKey()
    46  	bitcoinKey1 = priv1.PubKey()
    47  
    48  	priv2, _    = secp256k1.GeneratePrivateKey()
    49  	bitcoinKey2 = priv2.PubKey()
    50  
    51  	timeout = time.Second * 5
    52  )
    53  
    54  func createTestNode() (*channeldb.LightningNode, error) {
    55  	updateTime := prand.Int63()
    56  
    57  	priv, err := secp256k1.GeneratePrivateKey()
    58  	if err != nil {
    59  		return nil, errors.Errorf("unable create private key: %v", err)
    60  	}
    61  
    62  	pub := priv.PubKey().SerializeCompressed()
    63  	n := &channeldb.LightningNode{
    64  		HaveNodeAnnouncement: true,
    65  		LastUpdate:           time.Unix(updateTime, 0),
    66  		Addresses:            testAddrs,
    67  		Color:                color.RGBA{1, 2, 3, 0},
    68  		Alias:                "kek" + string(pub),
    69  		AuthSigBytes:         testSig.Serialize(),
    70  		Features:             testFeatures,
    71  	}
    72  	copy(n.PubKeyBytes[:], pub)
    73  
    74  	return n, nil
    75  }
    76  
    77  func randEdgePolicy(chanID *lnwire.ShortChannelID,
    78  	node *channeldb.LightningNode) *channeldb.ChannelEdgePolicy {
    79  
    80  	return &channeldb.ChannelEdgePolicy{
    81  		SigBytes:                  testSig.Serialize(),
    82  		ChannelID:                 chanID.ToUint64(),
    83  		LastUpdate:                time.Unix(int64(prand.Int31()), 0),
    84  		TimeLockDelta:             uint16(prand.Int63()),
    85  		MinHTLC:                   lnwire.MilliAtom(prand.Int31()),
    86  		MaxHTLC:                   lnwire.MilliAtom(prand.Int31()),
    87  		FeeBaseMAtoms:             lnwire.MilliAtom(prand.Int31()),
    88  		FeeProportionalMillionths: lnwire.MilliAtom(prand.Int31()),
    89  		Node:                      node,
    90  	}
    91  }
    92  
    93  func createChannelEdge(ctx *testCtx, bitcoinKey1, bitcoinKey2 []byte,
    94  	chanValue dcrutil.Amount, fundingHeight uint32) (*wire.MsgTx, *wire.OutPoint,
    95  	*lnwire.ShortChannelID, error) {
    96  
    97  	fundingTx := wire.NewMsgTx()
    98  	fundingTx.Version = 2
    99  	_, tx, err := input.GenFundingPkScript(
   100  		bitcoinKey1,
   101  		bitcoinKey2,
   102  		int64(chanValue),
   103  	)
   104  	if err != nil {
   105  		return nil, nil, nil, err
   106  	}
   107  
   108  	fundingTx.TxOut = append(fundingTx.TxOut, tx)
   109  	chanUtxo := wire.OutPoint{
   110  		Hash:  fundingTx.TxHash(),
   111  		Index: 0,
   112  	}
   113  
   114  	// With the utxo constructed, we'll mark it as closed.
   115  	ctx.chain.addUtxo(chanUtxo, tx)
   116  
   117  	// Our fake channel will be "confirmed" at height 101.
   118  	chanID := &lnwire.ShortChannelID{
   119  		BlockHeight: fundingHeight,
   120  		TxIndex:     0,
   121  		TxPosition:  0,
   122  	}
   123  
   124  	return fundingTx, &chanUtxo, chanID, nil
   125  }
   126  
   127  type mockChain struct {
   128  	blocks           map[chainhash.Hash]*wire.MsgBlock
   129  	blockIndex       map[uint32]chainhash.Hash
   130  	blockHeightIndex map[chainhash.Hash]uint32
   131  
   132  	utxos map[wire.OutPoint]wire.TxOut
   133  
   134  	bestHeight int32
   135  
   136  	sync.RWMutex
   137  }
   138  
   139  // A compile time check to ensure mockChain implements the
   140  // lnwallet.BlockChainIO interface.
   141  var _ lnwallet.BlockChainIO = (*mockChain)(nil)
   142  
   143  func newMockChain(currentHeight uint32) *mockChain {
   144  	return &mockChain{
   145  		bestHeight:       int32(currentHeight),
   146  		blocks:           make(map[chainhash.Hash]*wire.MsgBlock),
   147  		utxos:            make(map[wire.OutPoint]wire.TxOut),
   148  		blockIndex:       make(map[uint32]chainhash.Hash),
   149  		blockHeightIndex: make(map[chainhash.Hash]uint32),
   150  	}
   151  }
   152  
   153  func (m *mockChain) setBestBlock(height int32) {
   154  	m.Lock()
   155  	defer m.Unlock()
   156  
   157  	m.bestHeight = height
   158  }
   159  
   160  func (m *mockChain) GetBestBlock() (*chainhash.Hash, int32, error) {
   161  	m.RLock()
   162  	defer m.RUnlock()
   163  
   164  	blockHash := m.blockIndex[uint32(m.bestHeight)]
   165  
   166  	return &blockHash, m.bestHeight, nil
   167  }
   168  
   169  func (m *mockChain) GetTransaction(txid *chainhash.Hash) (*wire.MsgTx, error) {
   170  	return nil, nil
   171  }
   172  
   173  func (m *mockChain) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) {
   174  	m.RLock()
   175  	defer m.RUnlock()
   176  
   177  	hash, ok := m.blockIndex[uint32(blockHeight)]
   178  	if !ok {
   179  		return nil, fmt.Errorf("block number out of range: %v",
   180  			blockHeight)
   181  	}
   182  
   183  	return &hash, nil
   184  }
   185  
   186  func (m *mockChain) addUtxo(op wire.OutPoint, out *wire.TxOut) {
   187  	m.Lock()
   188  	m.utxos[op] = *out
   189  	m.Unlock()
   190  }
   191  
   192  func (m *mockChain) delUtxo(op wire.OutPoint) {
   193  	m.Lock()
   194  	delete(m.utxos, op)
   195  	m.Unlock()
   196  }
   197  
   198  func (m *mockChain) GetUtxo(op *wire.OutPoint, _ []byte, _ uint32,
   199  	_ <-chan struct{}) (*wire.TxOut, error) {
   200  	m.RLock()
   201  	defer m.RUnlock()
   202  
   203  	utxo, ok := m.utxos[*op]
   204  	if !ok {
   205  		return nil, lnwallet.ErrUtxoAlreadySpent{}
   206  	}
   207  
   208  	return &utxo, nil
   209  }
   210  
   211  func (m *mockChain) addBlock(block *wire.MsgBlock, height uint32, nonce uint32) {
   212  	m.Lock()
   213  	block.Header.Nonce = nonce
   214  	hash := block.Header.BlockHash()
   215  	m.blocks[hash] = block
   216  	m.blockIndex[height] = hash
   217  	m.blockHeightIndex[hash] = height
   218  	m.Unlock()
   219  }
   220  
   221  func (m *mockChain) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
   222  	m.RLock()
   223  	defer m.RUnlock()
   224  
   225  	block, ok := m.blocks[*blockHash]
   226  	if !ok {
   227  		return nil, fmt.Errorf("block not found")
   228  	}
   229  
   230  	return block, nil
   231  }
   232  
   233  type mockChainView struct {
   234  	sync.RWMutex
   235  
   236  	newBlocks           chan *chainview.FilteredBlock
   237  	staleBlocks         chan *chainview.FilteredBlock
   238  	notifyBlockAck      chan struct{}
   239  	notifyStaleBlockAck chan struct{}
   240  
   241  	chain lnwallet.BlockChainIO
   242  
   243  	filter map[wire.OutPoint]struct{}
   244  
   245  	quit chan struct{}
   246  }
   247  
   248  // A compile time check to ensure mockChainView implements the
   249  // chainview.FilteredChainView.
   250  var _ chainview.FilteredChainView = (*mockChainView)(nil)
   251  
   252  func newMockChainView(chain lnwallet.BlockChainIO) *mockChainView {
   253  	return &mockChainView{
   254  		chain:       chain,
   255  		newBlocks:   make(chan *chainview.FilteredBlock, 10),
   256  		staleBlocks: make(chan *chainview.FilteredBlock, 10),
   257  		filter:      make(map[wire.OutPoint]struct{}),
   258  		quit:        make(chan struct{}),
   259  	}
   260  }
   261  
   262  func (m *mockChainView) Reset() {
   263  	m.filter = make(map[wire.OutPoint]struct{})
   264  	m.quit = make(chan struct{})
   265  	m.newBlocks = make(chan *chainview.FilteredBlock, 10)
   266  	m.staleBlocks = make(chan *chainview.FilteredBlock, 10)
   267  }
   268  
   269  func (m *mockChainView) UpdateFilter(ops []channeldb.EdgePoint, updateHeight int64) error {
   270  	m.Lock()
   271  	defer m.Unlock()
   272  
   273  	for _, op := range ops {
   274  		m.filter[op.OutPoint] = struct{}{}
   275  	}
   276  
   277  	return nil
   278  }
   279  
   280  func (m *mockChainView) notifyBlock(hash chainhash.Hash, height uint32,
   281  	txns []*wire.MsgTx, t *testing.T) {
   282  
   283  	m.RLock()
   284  	defer m.RUnlock()
   285  
   286  	select {
   287  	case m.newBlocks <- &chainview.FilteredBlock{
   288  		Hash:         hash,
   289  		Height:       int64(height),
   290  		Transactions: txns,
   291  	}:
   292  	case <-m.quit:
   293  		return
   294  	}
   295  
   296  	// Do not ack the block if our notify channel is nil.
   297  	if m.notifyBlockAck == nil {
   298  		return
   299  	}
   300  
   301  	select {
   302  	case m.notifyBlockAck <- struct{}{}:
   303  	case <-time.After(timeout):
   304  		t.Fatal("expected block to be delivered")
   305  	case <-m.quit:
   306  		return
   307  	}
   308  }
   309  
   310  func (m *mockChainView) notifyStaleBlock(hash chainhash.Hash, height uint32,
   311  	txns []*wire.MsgTx, t *testing.T) {
   312  
   313  	m.RLock()
   314  	defer m.RUnlock()
   315  
   316  	select {
   317  	case m.staleBlocks <- &chainview.FilteredBlock{
   318  		Hash:         hash,
   319  		Height:       int64(height),
   320  		Transactions: txns,
   321  	}:
   322  	case <-m.quit:
   323  		return
   324  	}
   325  
   326  	// Do not ack the block if our notify channel is nil.
   327  	if m.notifyStaleBlockAck == nil {
   328  		return
   329  	}
   330  
   331  	select {
   332  	case m.notifyStaleBlockAck <- struct{}{}:
   333  	case <-time.After(timeout):
   334  		t.Fatal("expected stale block to be delivered")
   335  	case <-m.quit:
   336  		return
   337  	}
   338  }
   339  
   340  func (m *mockChainView) FilteredBlocks() <-chan *chainview.FilteredBlock {
   341  	return m.newBlocks
   342  }
   343  
   344  func (m *mockChainView) DisconnectedBlocks() <-chan *chainview.FilteredBlock {
   345  	return m.staleBlocks
   346  }
   347  
   348  func (m *mockChainView) FilterBlock(blockHash *chainhash.Hash) (*chainview.FilteredBlock, error) {
   349  
   350  	block, err := m.chain.GetBlock(blockHash)
   351  	if err != nil {
   352  		return nil, err
   353  	}
   354  
   355  	chain := m.chain.(*mockChain)
   356  
   357  	chain.Lock()
   358  	filteredBlock := &chainview.FilteredBlock{
   359  		Hash:   *blockHash,
   360  		Height: int64(chain.blockHeightIndex[*blockHash]),
   361  	}
   362  	chain.Unlock()
   363  	for _, tx := range block.Transactions {
   364  		for _, txIn := range tx.TxIn {
   365  			prevOp := txIn.PreviousOutPoint
   366  			if _, ok := m.filter[prevOp]; ok {
   367  				filteredBlock.Transactions = append(
   368  					filteredBlock.Transactions, tx,
   369  				)
   370  
   371  				m.Lock()
   372  				delete(m.filter, prevOp)
   373  				m.Unlock()
   374  
   375  				break
   376  			}
   377  		}
   378  	}
   379  
   380  	return filteredBlock, nil
   381  }
   382  
   383  func (m *mockChainView) Start() error {
   384  	return nil
   385  }
   386  
   387  func (m *mockChainView) Stop() error {
   388  	close(m.quit)
   389  	return nil
   390  }
   391  
   392  // TestEdgeUpdateNotification tests that when edges are updated or added,
   393  // a proper notification is sent of to all registered clients.
   394  func TestEdgeUpdateNotification(t *testing.T) {
   395  	t.Parallel()
   396  
   397  	ctx, cleanUp := createTestCtxSingleNode(t, 0)
   398  	defer cleanUp()
   399  
   400  	// First we'll create the utxo for the channel to be "closed"
   401  	const chanValue = 10000
   402  	fundingTx, chanPoint, chanID, err := createChannelEdge(ctx,
   403  		bitcoinKey1.SerializeCompressed(), bitcoinKey2.SerializeCompressed(),
   404  		chanValue, 0)
   405  	if err != nil {
   406  		t.Fatalf("unable create channel edge: %v", err)
   407  	}
   408  
   409  	// We'll also add a record for the block that included our funding
   410  	// transaction.
   411  	fundingBlock := &wire.MsgBlock{
   412  		Transactions: []*wire.MsgTx{fundingTx},
   413  	}
   414  	ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight)
   415  
   416  	// Next we'll create two test nodes that the fake channel will be open
   417  	// between.
   418  	node1, err := createTestNode()
   419  	if err != nil {
   420  		t.Fatalf("unable to create test node: %v", err)
   421  	}
   422  	node2, err := createTestNode()
   423  	if err != nil {
   424  		t.Fatalf("unable to create test node: %v", err)
   425  	}
   426  
   427  	// Finally, to conclude our test set up, we'll create a channel
   428  	// update to announce the created channel between the two nodes.
   429  	edge := &channeldb.ChannelEdgeInfo{
   430  		ChannelID:     chanID.ToUint64(),
   431  		NodeKey1Bytes: node1.PubKeyBytes,
   432  		NodeKey2Bytes: node2.PubKeyBytes,
   433  		AuthProof: &channeldb.ChannelAuthProof{
   434  			NodeSig1Bytes:   testSig.Serialize(),
   435  			NodeSig2Bytes:   testSig.Serialize(),
   436  			DecredSig1Bytes: testSig.Serialize(),
   437  			DecredSig2Bytes: testSig.Serialize(),
   438  		},
   439  	}
   440  	copy(edge.DecredKey1Bytes[:], bitcoinKey1.SerializeCompressed())
   441  	copy(edge.DecredKey2Bytes[:], bitcoinKey2.SerializeCompressed())
   442  
   443  	if err := ctx.router.AddEdge(edge); err != nil {
   444  		t.Fatalf("unable to add edge: %v", err)
   445  	}
   446  
   447  	// With the channel edge now in place, we'll subscribe for topology
   448  	// notifications.
   449  	ntfnClient, err := ctx.router.SubscribeTopology()
   450  	if err != nil {
   451  		t.Fatalf("unable to subscribe for channel notifications: %v", err)
   452  	}
   453  
   454  	// Create random policy edges that are stemmed to the channel id
   455  	// created above.
   456  	edge1 := randEdgePolicy(chanID, node1)
   457  	edge1.ChannelFlags = 0
   458  	edge2 := randEdgePolicy(chanID, node2)
   459  	edge2.ChannelFlags = 1
   460  
   461  	if err := ctx.router.UpdateEdge(edge1); err != nil {
   462  		t.Fatalf("unable to add edge update: %v", err)
   463  	}
   464  	if err := ctx.router.UpdateEdge(edge2); err != nil {
   465  		t.Fatalf("unable to add edge update: %v", err)
   466  	}
   467  
   468  	assertEdgeCorrect := func(t *testing.T, edgeUpdate *ChannelEdgeUpdate,
   469  		edgeAnn *channeldb.ChannelEdgePolicy) {
   470  		if edgeUpdate.ChanID != edgeAnn.ChannelID {
   471  			t.Fatalf("channel ID of edge doesn't match: "+
   472  				"expected %v, got %v", chanID.ToUint64(), edgeUpdate.ChanID)
   473  		}
   474  		if edgeUpdate.ChanPoint != *chanPoint {
   475  			t.Fatalf("channel don't match: expected %v, got %v",
   476  				chanPoint, edgeUpdate.ChanPoint)
   477  		}
   478  		// TODO(roasbeef): this is a hack, needs to be removed
   479  		// after commitment fees are dynamic.
   480  		if edgeUpdate.Capacity != chanValue {
   481  			t.Fatalf("capacity of edge doesn't match: "+
   482  				"expected %v, got %v", chanValue, edgeUpdate.Capacity)
   483  		}
   484  		if edgeUpdate.MinHTLC != edgeAnn.MinHTLC {
   485  			t.Fatalf("min HTLC of edge doesn't match: "+
   486  				"expected %v, got %v", edgeAnn.MinHTLC,
   487  				edgeUpdate.MinHTLC)
   488  		}
   489  		if edgeUpdate.MaxHTLC != edgeAnn.MaxHTLC {
   490  			t.Fatalf("max HTLC of edge doesn't match: "+
   491  				"expected %v, got %v", edgeAnn.MaxHTLC,
   492  				edgeUpdate.MaxHTLC)
   493  		}
   494  		if edgeUpdate.BaseFee != edgeAnn.FeeBaseMAtoms {
   495  			t.Fatalf("base fee of edge doesn't match: "+
   496  				"expected %v, got %v", edgeAnn.FeeBaseMAtoms,
   497  				edgeUpdate.BaseFee)
   498  		}
   499  		if edgeUpdate.FeeRate != edgeAnn.FeeProportionalMillionths {
   500  			t.Fatalf("fee rate of edge doesn't match: "+
   501  				"expected %v, got %v", edgeAnn.FeeProportionalMillionths,
   502  				edgeUpdate.FeeRate)
   503  		}
   504  		if edgeUpdate.TimeLockDelta != edgeAnn.TimeLockDelta {
   505  			t.Fatalf("time lock delta of edge doesn't match: "+
   506  				"expected %v, got %v", edgeAnn.TimeLockDelta,
   507  				edgeUpdate.TimeLockDelta)
   508  		}
   509  	}
   510  
   511  	// Create lookup map for notifications we are intending to receive. Entries
   512  	// are removed from the map when the anticipated notification is received.
   513  	var waitingFor = map[route.Vertex]int{
   514  		route.Vertex(node1.PubKeyBytes): 1,
   515  		route.Vertex(node2.PubKeyBytes): 2,
   516  	}
   517  
   518  	node1Pub, err := node1.PubKey()
   519  	if err != nil {
   520  		t.Fatalf("unable to encode key: %v", err)
   521  	}
   522  	node2Pub, err := node2.PubKey()
   523  	if err != nil {
   524  		t.Fatalf("unable to encode key: %v", err)
   525  	}
   526  
   527  	const numEdgePolicies = 2
   528  	for i := 0; i < numEdgePolicies; i++ {
   529  		select {
   530  		case ntfn := <-ntfnClient.TopologyChanges:
   531  			// For each processed announcement we should only receive a
   532  			// single announcement in a batch.
   533  			if len(ntfn.ChannelEdgeUpdates) != 1 {
   534  				t.Fatalf("expected 1 notification, instead have %v",
   535  					len(ntfn.ChannelEdgeUpdates))
   536  			}
   537  
   538  			edgeUpdate := ntfn.ChannelEdgeUpdates[0]
   539  			nodeVertex := route.NewVertex(edgeUpdate.AdvertisingNode)
   540  
   541  			if idx, ok := waitingFor[nodeVertex]; ok {
   542  				switch idx {
   543  				case 1:
   544  					// Received notification corresponding to edge1.
   545  					assertEdgeCorrect(t, edgeUpdate, edge1)
   546  					if !edgeUpdate.AdvertisingNode.IsEqual(node1Pub) {
   547  						t.Fatal("advertising node mismatch")
   548  					}
   549  					if !edgeUpdate.ConnectingNode.IsEqual(node2Pub) {
   550  						t.Fatal("connecting node mismatch")
   551  					}
   552  
   553  				case 2:
   554  					// Received notification corresponding to edge2.
   555  					assertEdgeCorrect(t, edgeUpdate, edge2)
   556  					if !edgeUpdate.AdvertisingNode.IsEqual(node2Pub) {
   557  						t.Fatal("advertising node mismatch")
   558  					}
   559  					if !edgeUpdate.ConnectingNode.IsEqual(node1Pub) {
   560  						t.Fatal("connecting node mismatch")
   561  					}
   562  
   563  				default:
   564  					t.Fatal("invalid edge index")
   565  				}
   566  
   567  				// Remove entry from waitingFor map to ensure
   568  				// we don't double count a repeat notification.
   569  				delete(waitingFor, nodeVertex)
   570  
   571  			} else {
   572  				t.Fatal("unexpected edge update received")
   573  			}
   574  
   575  		case <-time.After(time.Second * 5):
   576  			t.Fatal("edge update not received")
   577  		}
   578  	}
   579  }
   580  
   581  // TestNodeUpdateNotification tests that notifications are sent out when nodes
   582  // either join the network for the first time, or update their authenticated
   583  // attributes with new data.
   584  func TestNodeUpdateNotification(t *testing.T) {
   585  	t.Parallel()
   586  
   587  	const startingBlockHeight = 101
   588  	ctx, cleanUp := createTestCtxSingleNode(t, startingBlockHeight)
   589  	defer cleanUp()
   590  
   591  	// We only accept node announcements from nodes having a known channel,
   592  	// so create one now.
   593  	const chanValue = 10000
   594  	fundingTx, _, chanID, err := createChannelEdge(ctx,
   595  		bitcoinKey1.SerializeCompressed(),
   596  		bitcoinKey2.SerializeCompressed(),
   597  		chanValue, startingBlockHeight)
   598  	if err != nil {
   599  		t.Fatalf("unable create channel edge: %v", err)
   600  	}
   601  
   602  	// We'll also add a record for the block that included our funding
   603  	// transaction.
   604  	fundingBlock := &wire.MsgBlock{
   605  		Transactions: []*wire.MsgTx{fundingTx},
   606  	}
   607  	ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight)
   608  
   609  	// Create two nodes acting as endpoints in the created channel, and use
   610  	// them to trigger notifications by sending updated node announcement
   611  	// messages.
   612  	node1, err := createTestNode()
   613  	if err != nil {
   614  		t.Fatalf("unable to create test node: %v", err)
   615  	}
   616  	node2, err := createTestNode()
   617  	if err != nil {
   618  		t.Fatalf("unable to create test node: %v", err)
   619  	}
   620  
   621  	testFeaturesBuf := new(bytes.Buffer)
   622  	require.NoError(t, testFeatures.Encode(testFeaturesBuf))
   623  
   624  	edge := &channeldb.ChannelEdgeInfo{
   625  		ChannelID:     chanID.ToUint64(),
   626  		NodeKey1Bytes: node1.PubKeyBytes,
   627  		NodeKey2Bytes: node2.PubKeyBytes,
   628  		AuthProof: &channeldb.ChannelAuthProof{
   629  			NodeSig1Bytes:   testSig.Serialize(),
   630  			NodeSig2Bytes:   testSig.Serialize(),
   631  			DecredSig1Bytes: testSig.Serialize(),
   632  			DecredSig2Bytes: testSig.Serialize(),
   633  		},
   634  	}
   635  	copy(edge.DecredKey1Bytes[:], bitcoinKey1.SerializeCompressed())
   636  	copy(edge.DecredKey2Bytes[:], bitcoinKey2.SerializeCompressed())
   637  
   638  	// Adding the edge will add the nodes to the graph, but with no info
   639  	// except the pubkey known.
   640  	if err := ctx.router.AddEdge(edge); err != nil {
   641  		t.Fatalf("unable to add edge: %v", err)
   642  	}
   643  
   644  	// Create a new client to receive notifications.
   645  	ntfnClient, err := ctx.router.SubscribeTopology()
   646  	if err != nil {
   647  		t.Fatalf("unable to subscribe for channel notifications: %v", err)
   648  	}
   649  
   650  	// Change network topology by adding the updated info for the two nodes
   651  	// to the channel router.
   652  	if err := ctx.router.AddNode(node1); err != nil {
   653  		t.Fatalf("unable to add node: %v", err)
   654  	}
   655  	if err := ctx.router.AddNode(node2); err != nil {
   656  		t.Fatalf("unable to add node: %v", err)
   657  	}
   658  
   659  	assertNodeNtfnCorrect := func(t *testing.T, ann *channeldb.LightningNode,
   660  		nodeUpdate *NetworkNodeUpdate) {
   661  
   662  		nodeKey, _ := ann.PubKey()
   663  
   664  		// The notification received should directly map the
   665  		// announcement originally sent.
   666  		if nodeUpdate.Addresses[0] != ann.Addresses[0] {
   667  			t.Fatalf("node address doesn't match: expected %v, got %v",
   668  				nodeUpdate.Addresses[0], ann.Addresses[0])
   669  		}
   670  		if !nodeUpdate.IdentityKey.IsEqual(nodeKey) {
   671  			t.Fatalf("node identity keys don't match: expected %x, "+
   672  				"got %x", nodeKey.SerializeCompressed(),
   673  				nodeUpdate.IdentityKey.SerializeCompressed())
   674  		}
   675  
   676  		featuresBuf := new(bytes.Buffer)
   677  		require.NoError(t, nodeUpdate.Features.Encode(featuresBuf))
   678  
   679  		require.Equal(
   680  			t, testFeaturesBuf.Bytes(), featuresBuf.Bytes(),
   681  		)
   682  
   683  		if nodeUpdate.Alias != ann.Alias {
   684  			t.Fatalf("node alias doesn't match: expected %v, got %v",
   685  				ann.Alias, nodeUpdate.Alias)
   686  		}
   687  		if nodeUpdate.Color != EncodeHexColor(ann.Color) {
   688  			t.Fatalf("node color doesn't match: expected %v, got %v",
   689  				EncodeHexColor(ann.Color), nodeUpdate.Color)
   690  		}
   691  	}
   692  
   693  	// Create lookup map for notifications we are intending to receive. Entries
   694  	// are removed from the map when the anticipated notification is received.
   695  	var waitingFor = map[route.Vertex]int{
   696  		route.Vertex(node1.PubKeyBytes): 1,
   697  		route.Vertex(node2.PubKeyBytes): 2,
   698  	}
   699  
   700  	// Exactly two notifications should be sent, each corresponding to the
   701  	// node announcement messages sent above.
   702  	const numAnns = 2
   703  	for i := 0; i < numAnns; i++ {
   704  		select {
   705  		case ntfn := <-ntfnClient.TopologyChanges:
   706  			// For each processed announcement we should only receive a
   707  			// single announcement in a batch.
   708  			if len(ntfn.NodeUpdates) != 1 {
   709  				t.Fatalf("expected 1 notification, instead have %v",
   710  					len(ntfn.NodeUpdates))
   711  			}
   712  
   713  			nodeUpdate := ntfn.NodeUpdates[0]
   714  			nodeVertex := route.NewVertex(nodeUpdate.IdentityKey)
   715  			if idx, ok := waitingFor[nodeVertex]; ok {
   716  				switch idx {
   717  				case 1:
   718  					// Received notification corresponding to node1.
   719  					assertNodeNtfnCorrect(t, node1, nodeUpdate)
   720  
   721  				case 2:
   722  					// Received notification corresponding to node2.
   723  					assertNodeNtfnCorrect(t, node2, nodeUpdate)
   724  
   725  				default:
   726  					t.Fatal("invalid node index")
   727  				}
   728  
   729  				// Remove entry from waitingFor map to ensure we don't double count a
   730  				// repeat notification.
   731  				delete(waitingFor, nodeVertex)
   732  
   733  			} else {
   734  				t.Fatal("unexpected node update received")
   735  			}
   736  
   737  		case <-time.After(time.Second * 5):
   738  			t.Fatal("node update not received")
   739  		}
   740  	}
   741  
   742  	// If we receive a new update from a node (with a higher timestamp),
   743  	// then it should trigger a new notification.
   744  	// TODO(roasbeef): assume monotonic time.
   745  	nodeUpdateAnn := *node1
   746  	nodeUpdateAnn.LastUpdate = node1.LastUpdate.Add(300 * time.Millisecond)
   747  
   748  	// Add new node topology update to the channel router.
   749  	if err := ctx.router.AddNode(&nodeUpdateAnn); err != nil {
   750  		t.Fatalf("unable to add node: %v", err)
   751  	}
   752  
   753  	// Once again a notification should be received reflecting the up to
   754  	// date node announcement.
   755  	select {
   756  	case ntfn := <-ntfnClient.TopologyChanges:
   757  		// For each processed announcement we should only receive a
   758  		// single announcement in a batch.
   759  		if len(ntfn.NodeUpdates) != 1 {
   760  			t.Fatalf("expected 1 notification, instead have %v",
   761  				len(ntfn.NodeUpdates))
   762  		}
   763  
   764  		nodeUpdate := ntfn.NodeUpdates[0]
   765  		assertNodeNtfnCorrect(t, &nodeUpdateAnn, nodeUpdate)
   766  
   767  	case <-time.After(time.Second * 5):
   768  		t.Fatal("update not received")
   769  	}
   770  }
   771  
   772  // TestNotificationCancellation tests that notifications are properly canceled
   773  // when the client wishes to exit.
   774  func TestNotificationCancellation(t *testing.T) {
   775  	t.Parallel()
   776  
   777  	const startingBlockHeight = 101
   778  	ctx, cleanUp := createTestCtxSingleNode(t, startingBlockHeight)
   779  	defer cleanUp()
   780  
   781  	// Create a new client to receive notifications.
   782  	ntfnClient, err := ctx.router.SubscribeTopology()
   783  	if err != nil {
   784  		t.Fatalf("unable to subscribe for channel notifications: %v", err)
   785  	}
   786  
   787  	// We'll create the utxo for a new channel.
   788  	const chanValue = 10000
   789  	fundingTx, _, chanID, err := createChannelEdge(ctx,
   790  		bitcoinKey1.SerializeCompressed(),
   791  		bitcoinKey2.SerializeCompressed(),
   792  		chanValue, startingBlockHeight)
   793  	if err != nil {
   794  		t.Fatalf("unable create channel edge: %v", err)
   795  	}
   796  
   797  	// We'll also add a record for the block that included our funding
   798  	// transaction.
   799  	fundingBlock := &wire.MsgBlock{
   800  		Transactions: []*wire.MsgTx{fundingTx},
   801  	}
   802  	ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight)
   803  
   804  	// We'll create a fresh new node topology update to feed to the channel
   805  	// router.
   806  	node1, err := createTestNode()
   807  	if err != nil {
   808  		t.Fatalf("unable to create test node: %v", err)
   809  	}
   810  	node2, err := createTestNode()
   811  	if err != nil {
   812  		t.Fatalf("unable to create test node: %v", err)
   813  	}
   814  
   815  	// Before we send the message to the channel router, we'll cancel the
   816  	// notifications for this client. As a result, the notification
   817  	// triggered by accepting the channel announcements shouldn't be sent
   818  	// to the client.
   819  	ntfnClient.Cancel()
   820  
   821  	edge := &channeldb.ChannelEdgeInfo{
   822  		ChannelID:     chanID.ToUint64(),
   823  		NodeKey1Bytes: node1.PubKeyBytes,
   824  		NodeKey2Bytes: node2.PubKeyBytes,
   825  		AuthProof: &channeldb.ChannelAuthProof{
   826  			NodeSig1Bytes:   testSig.Serialize(),
   827  			NodeSig2Bytes:   testSig.Serialize(),
   828  			DecredSig1Bytes: testSig.Serialize(),
   829  			DecredSig2Bytes: testSig.Serialize(),
   830  		},
   831  	}
   832  	copy(edge.DecredKey1Bytes[:], bitcoinKey1.SerializeCompressed())
   833  	copy(edge.DecredKey2Bytes[:], bitcoinKey2.SerializeCompressed())
   834  	if err := ctx.router.AddEdge(edge); err != nil {
   835  		t.Fatalf("unable to add edge: %v", err)
   836  	}
   837  
   838  	if err := ctx.router.AddNode(node1); err != nil {
   839  		t.Fatalf("unable to add node: %v", err)
   840  	}
   841  
   842  	if err := ctx.router.AddNode(node2); err != nil {
   843  		t.Fatalf("unable to add node: %v", err)
   844  	}
   845  
   846  	select {
   847  	// The notifications shouldn't be sent, however, the channel should be
   848  	// closed, causing the second read-value to be false.
   849  	case _, ok := <-ntfnClient.TopologyChanges:
   850  		if !ok {
   851  			return
   852  		}
   853  
   854  		t.Fatal("notification sent but shouldn't have been")
   855  
   856  	case <-time.After(time.Second * 5):
   857  		t.Fatal("notification client never canceled")
   858  	}
   859  }
   860  
   861  // TestChannelCloseNotification tests that channel closure notifications are
   862  // properly dispatched to all registered clients.
   863  func TestChannelCloseNotification(t *testing.T) {
   864  	t.Parallel()
   865  
   866  	const startingBlockHeight = 101
   867  	ctx, cleanUp := createTestCtxSingleNode(t, startingBlockHeight)
   868  	defer cleanUp()
   869  
   870  	// First we'll create the utxo for the channel to be "closed"
   871  	const chanValue = 10000
   872  	fundingTx, chanUtxo, chanID, err := createChannelEdge(ctx,
   873  		bitcoinKey1.SerializeCompressed(), bitcoinKey2.SerializeCompressed(),
   874  		chanValue, startingBlockHeight)
   875  	if err != nil {
   876  		t.Fatalf("unable create channel edge: %v", err)
   877  	}
   878  
   879  	// We'll also add a record for the block that included our funding
   880  	// transaction.
   881  	fundingBlock := &wire.MsgBlock{
   882  		Transactions: []*wire.MsgTx{fundingTx},
   883  	}
   884  	ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight)
   885  
   886  	// Next we'll create two test nodes that the fake channel will be open
   887  	// between.
   888  	node1, err := createTestNode()
   889  	if err != nil {
   890  		t.Fatalf("unable to create test node: %v", err)
   891  	}
   892  	node2, err := createTestNode()
   893  	if err != nil {
   894  		t.Fatalf("unable to create test node: %v", err)
   895  	}
   896  
   897  	// Finally, to conclude our test set up, we'll create a channel
   898  	// announcement to announce the created channel between the two nodes.
   899  	edge := &channeldb.ChannelEdgeInfo{
   900  		ChannelID:     chanID.ToUint64(),
   901  		NodeKey1Bytes: node1.PubKeyBytes,
   902  		NodeKey2Bytes: node2.PubKeyBytes,
   903  		AuthProof: &channeldb.ChannelAuthProof{
   904  			NodeSig1Bytes:   testSig.Serialize(),
   905  			NodeSig2Bytes:   testSig.Serialize(),
   906  			DecredSig1Bytes: testSig.Serialize(),
   907  			DecredSig2Bytes: testSig.Serialize(),
   908  		},
   909  	}
   910  	copy(edge.DecredKey1Bytes[:], bitcoinKey1.SerializeCompressed())
   911  	copy(edge.DecredKey2Bytes[:], bitcoinKey2.SerializeCompressed())
   912  	if err := ctx.router.AddEdge(edge); err != nil {
   913  		t.Fatalf("unable to add edge: %v", err)
   914  	}
   915  
   916  	// With the channel edge now in place, we'll subscribe for topology
   917  	// notifications.
   918  	ntfnClient, err := ctx.router.SubscribeTopology()
   919  	if err != nil {
   920  		t.Fatalf("unable to subscribe for channel notifications: %v", err)
   921  	}
   922  
   923  	// Next, we'll simulate the closure of our channel by generating a new
   924  	// block at height 102 which spends the original multi-sig output of
   925  	// the channel.
   926  	blockHeight := uint32(102)
   927  	newBlock := &wire.MsgBlock{
   928  		Transactions: []*wire.MsgTx{
   929  			{
   930  				TxIn: []*wire.TxIn{
   931  					{
   932  						PreviousOutPoint: *chanUtxo,
   933  					},
   934  				},
   935  			},
   936  		},
   937  	}
   938  	ctx.chain.addBlock(newBlock, blockHeight, blockHeight)
   939  	ctx.chainView.notifyBlock(newBlock.Header.BlockHash(), blockHeight,
   940  		newBlock.Transactions, t)
   941  
   942  	// The notification registered above should be sent, if not we'll time
   943  	// out and mark the test as failed.
   944  	select {
   945  	case ntfn := <-ntfnClient.TopologyChanges:
   946  		// We should have exactly a single notification for the channel
   947  		// "closed" above.
   948  		closedChans := ntfn.ClosedChannels
   949  		if len(closedChans) == 0 {
   950  			t.Fatal("close channel ntfn not populated")
   951  		} else if len(closedChans) != 1 {
   952  			t.Fatalf("only one should have been detected as closed, "+
   953  				"instead %v were", len(closedChans))
   954  		}
   955  
   956  		// Ensure that the notification we received includes the proper
   957  		// update the for the channel that was closed in the generated
   958  		// block.
   959  		closedChan := closedChans[0]
   960  		if closedChan.ChanID != chanID.ToUint64() {
   961  			t.Fatalf("channel ID of closed channel doesn't match: "+
   962  				"expected %v, got %v", chanID.ToUint64(), closedChan.ChanID)
   963  		}
   964  		// TODO(roasbeef): this is a hack, needs to be removed
   965  		// after commitment fees are dynamic.
   966  		if closedChan.Capacity != chanValue {
   967  			t.Fatalf("capacity of closed channel doesn't match: "+
   968  				"expected %v, got %v", chanValue, closedChan.Capacity)
   969  		}
   970  		if closedChan.ClosedHeight != blockHeight {
   971  			t.Fatalf("close height of closed channel doesn't match: "+
   972  				"expected %v, got %v", blockHeight, closedChan.ClosedHeight)
   973  		}
   974  		if closedChan.ChanPoint != *chanUtxo {
   975  			t.Fatalf("chan point of closed channel doesn't match: "+
   976  				"expected %v, got %v", chanUtxo, closedChan.ChanPoint)
   977  		}
   978  
   979  	case <-time.After(time.Second * 5):
   980  		t.Fatal("notification not sent")
   981  	}
   982  }
   983  
   984  // TestEncodeHexColor tests that the string used to represent a node color is
   985  // correctly encoded.
   986  func TestEncodeHexColor(t *testing.T) {
   987  	var colorTestCases = []struct {
   988  		R       uint8
   989  		G       uint8
   990  		B       uint8
   991  		encoded string
   992  		isValid bool
   993  	}{
   994  		{0, 0, 0, "#000000", true},
   995  		{255, 255, 255, "#ffffff", true},
   996  		{255, 117, 215, "#ff75d7", true},
   997  		{0, 0, 0, "000000", false},
   998  		{1, 2, 3, "", false},
   999  		{1, 2, 3, "#", false},
  1000  	}
  1001  
  1002  	for _, tc := range colorTestCases {
  1003  		encoded := EncodeHexColor(color.RGBA{tc.R, tc.G, tc.B, 0})
  1004  		if (encoded == tc.encoded) != tc.isValid {
  1005  			t.Fatalf("incorrect color encoding, "+
  1006  				"want: %v, got: %v", tc.encoded, encoded)
  1007  		}
  1008  	}
  1009  }