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 }