builder_test.go
1 package graph 2 3 import ( 4 "bytes" 5 "crypto/sha256" 6 "encoding/hex" 7 "encoding/json" 8 "errors" 9 "fmt" 10 "image/color" 11 "math/rand" 12 "net" 13 "os" 14 "strings" 15 "testing" 16 "time" 17 18 "github.com/btcsuite/btcd/btcec/v2" 19 "github.com/btcsuite/btcd/btcutil" 20 "github.com/btcsuite/btcd/chaincfg" 21 "github.com/btcsuite/btcd/chaincfg/chainhash" 22 "github.com/btcsuite/btcd/wire" 23 "github.com/lightningnetwork/lnd/chainntnfs" 24 graphdb "github.com/lightningnetwork/lnd/graph/db" 25 "github.com/lightningnetwork/lnd/graph/db/models" 26 "github.com/lightningnetwork/lnd/htlcswitch" 27 "github.com/lightningnetwork/lnd/lntest/wait" 28 "github.com/lightningnetwork/lnd/lnwire" 29 "github.com/lightningnetwork/lnd/routing/route" 30 "github.com/stretchr/testify/require" 31 ) 32 33 const ( 34 // basicGraphFilePath is the file path for a basic graph used within 35 // the tests. The basic graph consists of 5 nodes with 5 channels 36 // connecting them. 37 basicGraphFilePath = "testdata/basic_graph.json" 38 39 testTimeout = 5 * time.Second 40 ) 41 42 // TestAddProof checks that we can update the channel proof after channel 43 // info was added to the database. 44 func TestAddProof(t *testing.T) { 45 t.Parallel() 46 ctxb := t.Context() 47 48 ctx := createTestCtxSingleNode(t, 0) 49 50 // Before creating out edge, we'll create two new nodes within the 51 // network that the channel will connect. 52 node1 := createTestNode(t) 53 node2 := createTestNode(t) 54 55 // In order to be able to add the edge we should have a valid funding 56 // UTXO within the blockchain. 57 script, fundingTx, _, chanID := createChannelEdge( 58 t, bitcoinKey1.SerializeCompressed(), 59 bitcoinKey2.SerializeCompressed(), 100, 0, 60 ) 61 fundingBlock := &wire.MsgBlock{ 62 Transactions: []*wire.MsgTx{fundingTx}, 63 } 64 ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight) 65 66 // After utxo was recreated adding the edge without the proof. 67 btcKey1 := route.NewVertex(bitcoinKey1) 68 btcKey2 := route.NewVertex(bitcoinKey2) 69 70 edge, err := models.NewV1Channel( 71 chanID.ToUint64(), *chaincfg.SimNetParams.GenesisHash, 72 node1.PubKeyBytes, node2.PubKeyBytes, &models.ChannelV1Fields{ 73 BitcoinKey1Bytes: btcKey1, 74 BitcoinKey2Bytes: btcKey2, 75 }, models.WithFundingScript(script), 76 ) 77 require.NoError(t, err) 78 require.NoError(t, ctx.builder.AddEdge(ctxb, edge)) 79 80 // Now we'll attempt to update the proof and check that it has been 81 // properly updated. 82 require.NoError(t, ctx.builder.AddProof(*chanID, &testAuthProof)) 83 84 info, _, _, err := ctx.builder.GetChannelByID(*chanID) 85 require.NoError(t, err, "unable to get channel") 86 require.NotNil(t, info.AuthProof) 87 } 88 89 // TestIgnoreNodeAnnouncement tests that adding a node to the router that is 90 // not known from any channel announcement, leads to the announcement being 91 // ignored. 92 func TestIgnoreNodeAnnouncement(t *testing.T) { 93 t.Parallel() 94 95 const startingBlockHeight = 101 96 ctx := createTestCtxFromFile(t, startingBlockHeight, basicGraphFilePath) 97 98 pub := priv1.PubKey() 99 node := models.NewV1Node( 100 route.NewVertex(pub), &models.NodeV1Fields{ 101 Addresses: testAddrs, 102 AuthSigBytes: testSig.Serialize(), 103 Features: testFeatures.RawFeatureVector, 104 LastUpdate: time.Unix(123, 0), 105 Color: color.RGBA{1, 2, 3, 0}, 106 Alias: "node11", 107 }, 108 ) 109 110 err := ctx.builder.AddNode(t.Context(), node) 111 require.Truef( 112 t, IsError(err, ErrIgnored), 113 "expected to get ErrIgnore, instead got: %v", err, 114 ) 115 } 116 117 // TestIgnoreChannelEdgePolicyForUnknownChannel checks that a router will 118 // ignore a channel policy for a channel not in the graph. 119 func TestIgnoreChannelEdgePolicyForUnknownChannel(t *testing.T) { 120 t.Parallel() 121 ctxb := t.Context() 122 123 const startingBlockHeight = 101 124 125 // Setup an initially empty network. 126 var testChannels []*testChannel 127 testGraph, err := createTestGraphFromChannels( 128 t, true, testChannels, "roasbeef", 129 ) 130 require.NoError(t, err, "unable to create graph") 131 132 ctx := createTestCtxFromGraphInstance( 133 t, startingBlockHeight, testGraph, false, 134 ) 135 136 var pub1 [33]byte 137 copy(pub1[:], priv1.PubKey().SerializeCompressed()) 138 139 var pub2 [33]byte 140 copy(pub2[:], priv2.PubKey().SerializeCompressed()) 141 142 // Add the edge between the two unknown nodes to the graph, and check 143 // that the nodes are found after the fact. 144 script, fundingTx, _, chanID := createChannelEdge( 145 t, bitcoinKey1.SerializeCompressed(), 146 bitcoinKey2.SerializeCompressed(), 10000, 500, 147 ) 148 fundingBlock := &wire.MsgBlock{ 149 Transactions: []*wire.MsgTx{fundingTx}, 150 } 151 ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight) 152 153 pub1Vertex, err := route.NewVertexFromBytes(pub1[:]) 154 require.NoError(t, err) 155 pub2Vertex, err := route.NewVertexFromBytes(pub2[:]) 156 require.NoError(t, err) 157 158 edge, err := models.NewV1Channel( 159 chanID.ToUint64(), *chaincfg.SimNetParams.GenesisHash, 160 pub1Vertex, pub2Vertex, &models.ChannelV1Fields{ 161 BitcoinKey1Bytes: pub1Vertex, 162 BitcoinKey2Bytes: pub2Vertex, 163 }, models.WithFundingScript(script), 164 ) 165 require.NoError(t, err) 166 167 edgePolicy := &models.ChannelEdgePolicy{ 168 Version: lnwire.GossipVersion1, 169 SigBytes: testSig.Serialize(), 170 ChannelID: edge.ChannelID, 171 LastUpdate: testTime, 172 TimeLockDelta: 10, 173 MinHTLC: 1, 174 FeeBaseMSat: 10, 175 FeeProportionalMillionths: 10000, 176 } 177 178 // Attempt to update the edge. This should be ignored, since the edge 179 // is not yet added to the router. 180 err = ctx.builder.UpdateEdge(ctxb, edgePolicy) 181 require.Truef( 182 t, IsError(err, ErrIgnored), 183 "expected to get ErrIgnore, instead got: %v", err, 184 ) 185 186 // Add the edge. 187 require.NoErrorf( 188 t, ctx.builder.AddEdge(ctxb, edge), 189 "expected to be able to add edge to the channel graph, even "+ 190 "though the vertexes were unknown: %v.", err, 191 ) 192 193 // Now updating the edge policy should succeed. 194 require.NoError(t, ctx.builder.UpdateEdge(ctxb, edgePolicy)) 195 } 196 197 // TestWakeUpOnStaleBranch tests that upon startup of the ChannelRouter, if the 198 // chain previously reflected in the channel graph is stale (overtaken by a 199 // longer chain), the channel router will prune the graph for any channels 200 // confirmed on the stale chain, and resync to the main chain. 201 func TestWakeUpOnStaleBranch(t *testing.T) { 202 t.Parallel() 203 ctxb := t.Context() 204 205 const startingBlockHeight = 101 206 ctx := createTestCtxSingleNode(t, startingBlockHeight) 207 208 const chanValue = 10000 209 210 // chanID1 will not be reorged out. 211 var chanID1 uint64 212 213 // chanID2 will be reorged out. 214 var chanID2 uint64 215 216 // Create 10 common blocks, confirming chanID1. 217 var fundingScript1 []byte 218 for i := uint32(1); i <= 10; i++ { 219 block := &wire.MsgBlock{ 220 Transactions: []*wire.MsgTx{}, 221 } 222 height := startingBlockHeight + i 223 if i == 5 { 224 script, fundingTx, _, chanID := createChannelEdge( 225 t, bitcoinKey1.SerializeCompressed(), 226 bitcoinKey2.SerializeCompressed(), 227 chanValue, height, 228 ) 229 block.Transactions = append(block.Transactions, 230 fundingTx) 231 chanID1 = chanID.ToUint64() 232 fundingScript1 = script 233 } 234 ctx.chain.addBlock(block, height, rand.Uint32()) 235 ctx.chain.setBestBlock(int32(height)) 236 ctx.chainView.notifyBlock(block.BlockHash(), height, 237 []*wire.MsgTx{}, t) 238 } 239 240 // Give time to process new blocks 241 time.Sleep(time.Millisecond * 500) 242 243 _, forkHeight, err := ctx.chain.GetBestBlock() 244 require.NoError(t, err, "unable to ge best block") 245 246 // Create 10 blocks on the minority chain, confirming chanID2. 247 var fundingScript2 []byte 248 for i := uint32(1); i <= 10; i++ { 249 block := &wire.MsgBlock{ 250 Transactions: []*wire.MsgTx{}, 251 } 252 height := uint32(forkHeight) + i 253 if i == 5 { 254 script, fundingTx, _, chanID := createChannelEdge( 255 t, bitcoinKey1.SerializeCompressed(), 256 bitcoinKey2.SerializeCompressed(), 257 chanValue, height) 258 block.Transactions = append(block.Transactions, 259 fundingTx) 260 chanID2 = chanID.ToUint64() 261 fundingScript2 = script 262 } 263 ctx.chain.addBlock(block, height, rand.Uint32()) 264 ctx.chain.setBestBlock(int32(height)) 265 ctx.chainView.notifyBlock(block.BlockHash(), height, 266 []*wire.MsgTx{}, t) 267 } 268 // Give time to process new blocks 269 time.Sleep(time.Millisecond * 500) 270 271 // Now add the two edges to the channel graph, and check that they 272 // correctly show up in the database. 273 node1 := createTestNode(t) 274 node2 := createTestNode(t) 275 276 btcKey1, err := route.NewVertexFromBytes( 277 bitcoinKey1.SerializeCompressed(), 278 ) 279 require.NoError(t, err) 280 btcKey2, err := route.NewVertexFromBytes( 281 bitcoinKey2.SerializeCompressed(), 282 ) 283 require.NoError(t, err) 284 285 edge1, err := models.NewV1Channel( 286 chanID1, *chaincfg.SimNetParams.GenesisHash, 287 node1.PubKeyBytes, node2.PubKeyBytes, &models.ChannelV1Fields{ 288 BitcoinKey1Bytes: btcKey1, 289 BitcoinKey2Bytes: btcKey2, 290 }, models.WithChanProof(models.NewV1ChannelAuthProof( 291 testSig.Serialize(), testSig.Serialize(), 292 testSig.Serialize(), testSig.Serialize(), 293 )), models.WithFundingScript(fundingScript1), 294 ) 295 require.NoError(t, err) 296 297 require.NoError(t, ctx.builder.AddEdge(ctxb, edge1)) 298 299 edge2, err := models.NewV1Channel( 300 chanID2, *chaincfg.SimNetParams.GenesisHash, node1.PubKeyBytes, 301 node2.PubKeyBytes, &models.ChannelV1Fields{ 302 BitcoinKey1Bytes: btcKey1, 303 BitcoinKey2Bytes: btcKey2, 304 }, models.WithChanProof(models.NewV1ChannelAuthProof( 305 testSig.Serialize(), testSig.Serialize(), 306 testSig.Serialize(), testSig.Serialize(), 307 )), models.WithFundingScript(fundingScript2), 308 ) 309 require.NoError(t, err) 310 311 require.NoError(t, ctx.builder.AddEdge(ctxb, edge2)) 312 313 // Check that the fundingTxs are in the graph db. 314 has, isZombie, err := ctx.graph.HasChannelEdge(t.Context(), chanID1) 315 require.NoError(t, err) 316 require.True(t, has) 317 require.False(t, isZombie) 318 319 has, isZombie, err = ctx.graph.HasChannelEdge(t.Context(), chanID2) 320 require.NoError(t, err) 321 require.True(t, has) 322 require.False(t, isZombie) 323 324 // Stop the router, so we can reorg the chain while its offline. 325 require.NoError(t, ctx.builder.Stop()) 326 327 // Create a 15 block fork. 328 for i := uint32(1); i <= 15; i++ { 329 block := &wire.MsgBlock{ 330 Transactions: []*wire.MsgTx{}, 331 } 332 height := uint32(forkHeight) + i 333 ctx.chain.addBlock(block, height, rand.Uint32()) 334 ctx.chain.setBestBlock(int32(height)) 335 } 336 337 // Give time to process new blocks. 338 time.Sleep(time.Millisecond * 500) 339 340 selfNode, err := ctx.graph.SourceNode(t.Context()) 341 require.NoError(t, err) 342 343 // Create new router with same graph database. 344 router, err := NewBuilder(&Config{ 345 SelfNode: selfNode.PubKeyBytes, 346 Graph: ctx.graph.ChannelGraph, 347 Chain: ctx.chain, 348 ChainView: ctx.chainView, 349 ChannelPruneExpiry: time.Hour * 24, 350 GraphPruneInterval: time.Hour * 2, 351 352 // We'll set the delay to zero to prune immediately. 353 FirstTimePruneDelay: 0, 354 IsAlias: func(scid lnwire.ShortChannelID) bool { 355 return false 356 }, 357 }) 358 require.NoError(t, err) 359 360 // It should resync to the longer chain on startup. 361 require.NoError(t, router.Start()) 362 363 // The channel with chanID2 should not be in the database anymore, 364 // since it is not confirmed on the longest chain. chanID1 should 365 // still be. 366 has, isZombie, err = ctx.graph.HasChannelEdge(t.Context(), chanID1) 367 require.NoError(t, err) 368 require.True(t, has) 369 require.False(t, isZombie) 370 371 has, isZombie, err = ctx.graph.HasChannelEdge(t.Context(), chanID2) 372 require.NoError(t, err) 373 require.False(t, has) 374 require.False(t, isZombie) 375 } 376 377 // TestDisconnectedBlocks checks that the router handles a reorg happening when 378 // it is active. 379 func TestDisconnectedBlocks(t *testing.T) { 380 t.Parallel() 381 ctxb := t.Context() 382 383 const startingBlockHeight = 101 384 ctx := createTestCtxSingleNode(t, startingBlockHeight) 385 386 const chanValue = 10000 387 388 // chanID1 will not be reorged out, while chanID2 will be reorged out. 389 var chanID1, chanID2 uint64 390 391 // Create 10 common blocks, confirming chanID1. 392 for i := uint32(1); i <= 10; i++ { 393 block := &wire.MsgBlock{ 394 Transactions: []*wire.MsgTx{}, 395 } 396 height := startingBlockHeight + i 397 if i == 5 { 398 _, fundingTx, _, chanID := createChannelEdge( 399 t, bitcoinKey1.SerializeCompressed(), 400 bitcoinKey2.SerializeCompressed(), 401 chanValue, height, 402 ) 403 block.Transactions = append(block.Transactions, 404 fundingTx) 405 chanID1 = chanID.ToUint64() 406 } 407 ctx.chain.addBlock(block, height, rand.Uint32()) 408 ctx.chain.setBestBlock(int32(height)) 409 ctx.chainView.notifyBlock(block.BlockHash(), height, 410 []*wire.MsgTx{}, t) 411 } 412 413 // Give time to process new blocks 414 time.Sleep(time.Millisecond * 500) 415 416 _, forkHeight, err := ctx.chain.GetBestBlock() 417 require.NoError(t, err, "unable to get best block") 418 419 // Create 10 blocks on the minority chain, confirming chanID2. 420 var minorityChain []*wire.MsgBlock 421 for i := uint32(1); i <= 10; i++ { 422 block := &wire.MsgBlock{ 423 Transactions: []*wire.MsgTx{}, 424 } 425 height := uint32(forkHeight) + i 426 if i == 5 { 427 _, fundingTx, _, chanID := createChannelEdge( 428 t, bitcoinKey1.SerializeCompressed(), 429 bitcoinKey2.SerializeCompressed(), 430 chanValue, height, 431 ) 432 block.Transactions = append(block.Transactions, 433 fundingTx) 434 chanID2 = chanID.ToUint64() 435 } 436 minorityChain = append(minorityChain, block) 437 ctx.chain.addBlock(block, height, rand.Uint32()) 438 ctx.chain.setBestBlock(int32(height)) 439 ctx.chainView.notifyBlock(block.BlockHash(), height, 440 []*wire.MsgTx{}, t) 441 } 442 // Give time to process new blocks 443 time.Sleep(time.Millisecond * 500) 444 445 // Now add the two edges to the channel graph, and check that they 446 // correctly show up in the database. 447 node1 := createTestNode(t) 448 node2 := createTestNode(t) 449 450 btcKey1 := route.NewVertex(bitcoinKey1) 451 btcKey2 := route.NewVertex(bitcoinKey2) 452 453 proof := models.NewV1ChannelAuthProof( 454 testSig.Serialize(), 455 testSig.Serialize(), 456 testSig.Serialize(), 457 testSig.Serialize(), 458 ) 459 460 edge1, err := models.NewV1Channel( 461 chanID1, *chaincfg.SimNetParams.GenesisHash, node1.PubKeyBytes, 462 node2.PubKeyBytes, &models.ChannelV1Fields{ 463 BitcoinKey1Bytes: btcKey1, 464 BitcoinKey2Bytes: btcKey2, 465 }, models.WithChanProof(proof), 466 models.WithFundingScript([]byte{}), 467 ) 468 require.NoError(t, err) 469 470 require.NoError(t, ctx.builder.AddEdge(ctxb, edge1)) 471 472 edge2, err := models.NewV1Channel( 473 chanID2, *chaincfg.SimNetParams.GenesisHash, node1.PubKeyBytes, 474 node2.PubKeyBytes, &models.ChannelV1Fields{ 475 BitcoinKey1Bytes: btcKey1, 476 BitcoinKey2Bytes: btcKey2, 477 }, models.WithChanProof(proof), 478 models.WithFundingScript([]byte{}), 479 ) 480 require.NoError(t, err) 481 482 require.NoError(t, ctx.builder.AddEdge(ctxb, edge2)) 483 484 // Check that the fundingTxs are in the graph db. 485 has, isZombie, err := ctx.graph.HasChannelEdge(t.Context(), chanID1) 486 require.NoError(t, err) 487 require.True(t, has) 488 require.False(t, isZombie) 489 490 has, isZombie, err = ctx.graph.HasChannelEdge(t.Context(), chanID2) 491 require.NoError(t, err) 492 require.True(t, has) 493 require.False(t, isZombie) 494 495 // Create a 15 block fork. We first let the chainView notify the router 496 // about stale blocks, before sending the now connected blocks. We do 497 // this because we expect this order from the chainview. 498 ctx.chainView.notifyStaleBlockAck = make(chan struct{}, 1) 499 for i := len(minorityChain) - 1; i >= 0; i-- { 500 block := minorityChain[i] 501 height := uint32(forkHeight) + uint32(i) + 1 502 ctx.chainView.notifyStaleBlock(block.BlockHash(), height, 503 block.Transactions, t) 504 <-ctx.chainView.notifyStaleBlockAck 505 } 506 507 time.Sleep(time.Second * 2) 508 509 ctx.chainView.notifyBlockAck = make(chan struct{}, 1) 510 for i := uint32(1); i <= 15; i++ { 511 block := &wire.MsgBlock{ 512 Transactions: []*wire.MsgTx{}, 513 } 514 height := uint32(forkHeight) + i 515 ctx.chain.addBlock(block, height, rand.Uint32()) 516 ctx.chain.setBestBlock(int32(height)) 517 ctx.chainView.notifyBlock(block.BlockHash(), height, 518 block.Transactions, t) 519 <-ctx.chainView.notifyBlockAck 520 } 521 522 time.Sleep(time.Millisecond * 500) 523 524 // chanID2 should not be in the database anymore, since it is not 525 // confirmed on the longest chain. chanID1 should still be. 526 has, isZombie, err = ctx.graph.HasChannelEdge(t.Context(), chanID1) 527 require.NoError(t, err) 528 require.True(t, has) 529 require.False(t, isZombie) 530 531 has, isZombie, err = ctx.graph.HasChannelEdge(t.Context(), chanID2) 532 require.NoError(t, err) 533 require.False(t, has) 534 require.False(t, isZombie) 535 } 536 537 // TestChansClosedOfflinePruneGraph tests that if channels we know of are 538 // closed while we're offline, then once we resume operation of the 539 // ChannelRouter, then the channels are properly pruned. 540 func TestChansClosedOfflinePruneGraph(t *testing.T) { 541 t.Parallel() 542 ctxb := t.Context() 543 544 const startingBlockHeight = 101 545 ctx := createTestCtxSingleNode(t, startingBlockHeight) 546 547 const chanValue = 10000 548 549 // First, we'll create a channel, to be mined shortly at height 102. 550 block102 := &wire.MsgBlock{ 551 Transactions: []*wire.MsgTx{}, 552 } 553 nextHeight := startingBlockHeight + 1 554 script, fundingTx1, chanUTXO, chanID1 := createChannelEdge( 555 t, bitcoinKey1.SerializeCompressed(), 556 bitcoinKey2.SerializeCompressed(), 557 chanValue, uint32(nextHeight), 558 ) 559 block102.Transactions = append(block102.Transactions, fundingTx1) 560 ctx.chain.addBlock(block102, uint32(nextHeight), rand.Uint32()) 561 ctx.chain.setBestBlock(int32(nextHeight)) 562 ctx.chainView.notifyBlock(block102.BlockHash(), uint32(nextHeight), 563 []*wire.MsgTx{}, t) 564 565 // We'll now create the edges and nodes within the database required 566 // for the ChannelRouter to properly recognize the channel we added 567 // above. 568 node1 := createTestNode(t) 569 node2 := createTestNode(t) 570 571 btcKey1 := route.NewVertex(bitcoinKey1) 572 btcKey2 := route.NewVertex(bitcoinKey2) 573 574 proof := models.NewV1ChannelAuthProof( 575 testSig.Serialize(), 576 testSig.Serialize(), 577 testSig.Serialize(), 578 testSig.Serialize(), 579 ) 580 581 edge1, err := models.NewV1Channel( 582 chanID1.ToUint64(), *chaincfg.SimNetParams.GenesisHash, 583 node1.PubKeyBytes, node2.PubKeyBytes, &models.ChannelV1Fields{ 584 BitcoinKey1Bytes: btcKey1, 585 BitcoinKey2Bytes: btcKey2, 586 }, models.WithChanProof(proof), 587 models.WithCapacity(chanValue), 588 models.WithChannelPoint(*chanUTXO), 589 models.WithFundingScript(script), 590 ) 591 require.NoError(t, err) 592 593 require.NoError(t, ctx.builder.AddEdge(ctxb, edge1)) 594 595 // The router should now be aware of the channel we created above. 596 hasChan, isZombie, err := ctx.graph.HasChannelEdge( 597 t.Context(), chanID1.ToUint64(), 598 ) 599 require.NoError(t, err) 600 require.True(t, hasChan) 601 require.False(t, isZombie) 602 603 // With the transaction included, and the router's database state 604 // updated, we'll now mine 5 additional blocks on top of it. 605 for i := 0; i < 5; i++ { 606 nextHeight++ 607 608 block := &wire.MsgBlock{ 609 Transactions: []*wire.MsgTx{}, 610 } 611 ctx.chain.addBlock(block, uint32(nextHeight), rand.Uint32()) 612 ctx.chain.setBestBlock(int32(nextHeight)) 613 ctx.chainView.notifyBlock(block.BlockHash(), uint32(nextHeight), 614 []*wire.MsgTx{}, t) 615 } 616 617 // At this point, our starting height should be 107. 618 _, chainHeight, err := ctx.chain.GetBestBlock() 619 require.NoError(t, err, "unable to get best block") 620 require.EqualValues(t, 107, chainHeight) 621 622 // Next, we'll "shut down" the router in order to simulate downtime. 623 require.NoError(t, ctx.builder.Stop()) 624 625 // While the router is "offline" we'll mine 5 additional blocks, with 626 // the second block closing the channel we created above. 627 for i := 0; i < 5; i++ { 628 nextHeight++ 629 630 block := &wire.MsgBlock{ 631 Transactions: []*wire.MsgTx{}, 632 } 633 634 if i == 2 { 635 // For the second block, we'll add a transaction that 636 // closes the channel we created above by spending the 637 // output. 638 closingTx := wire.NewMsgTx(2) 639 closingTx.AddTxIn(&wire.TxIn{ 640 PreviousOutPoint: *chanUTXO, 641 }) 642 block.Transactions = append(block.Transactions, 643 closingTx) 644 } 645 646 ctx.chain.addBlock(block, uint32(nextHeight), rand.Uint32()) 647 ctx.chain.setBestBlock(int32(nextHeight)) 648 ctx.chainView.notifyBlock(block.BlockHash(), uint32(nextHeight), 649 []*wire.MsgTx{}, t) 650 } 651 652 // At this point, our starting height should be 112. 653 _, chainHeight, err = ctx.chain.GetBestBlock() 654 require.NoError(t, err, "unable to get best block") 655 require.EqualValues(t, 112, chainHeight) 656 657 // Now we'll re-start the ChannelRouter. It should recognize that it's 658 // behind the main chain and prune all the blocks that it missed while 659 // it was down. 660 ctx.RestartBuilder(t) 661 662 // At this point, the channel that was pruned should no longer be known 663 // by the router. 664 hasChan, isZombie, err = ctx.graph.HasChannelEdge( 665 t.Context(), chanID1.ToUint64(), 666 ) 667 require.NoError(t, err) 668 require.False(t, hasChan) 669 require.False(t, isZombie) 670 } 671 672 // TestPruneChannelGraphStaleEdges ensures that we properly prune stale edges 673 // from the channel graph. 674 func TestPruneChannelGraphStaleEdges(t *testing.T) { 675 t.Parallel() 676 677 freshTimestamp := time.Now() 678 staleTimestamp := time.Unix(0, 0) 679 680 // We'll create the following test graph so that two of the channels 681 // are pruned. 682 testChannels := []*testChannel{ 683 // No edges. 684 { 685 Node1: &testChannelEnd{Alias: "a"}, 686 Node2: &testChannelEnd{Alias: "b"}, 687 Capacity: 100000, 688 ChannelID: 1, 689 }, 690 691 // Only one edge with a stale timestamp. 692 { 693 Node1: &testChannelEnd{ 694 Alias: "d", 695 testChannelPolicy: &testChannelPolicy{ 696 LastUpdate: staleTimestamp, 697 }, 698 }, 699 Node2: &testChannelEnd{Alias: "b"}, 700 Capacity: 100000, 701 ChannelID: 2, 702 }, 703 704 // Only one edge with a stale timestamp, but it's the source 705 // node so it won't get pruned. 706 { 707 Node1: &testChannelEnd{ 708 Alias: "a", 709 testChannelPolicy: &testChannelPolicy{ 710 LastUpdate: staleTimestamp, 711 }, 712 }, 713 Node2: &testChannelEnd{Alias: "b"}, 714 Capacity: 100000, 715 ChannelID: 3, 716 }, 717 718 // Only one edge with a fresh timestamp. 719 { 720 Node1: &testChannelEnd{ 721 Alias: "a", 722 testChannelPolicy: &testChannelPolicy{ 723 LastUpdate: freshTimestamp, 724 }, 725 }, 726 Node2: &testChannelEnd{Alias: "b"}, 727 Capacity: 100000, 728 ChannelID: 4, 729 }, 730 731 // One edge fresh, one edge stale. This will be pruned with 732 // strict pruning activated. 733 { 734 Node1: &testChannelEnd{ 735 Alias: "c", 736 testChannelPolicy: &testChannelPolicy{ 737 LastUpdate: freshTimestamp, 738 }, 739 }, 740 Node2: &testChannelEnd{ 741 Alias: "d", 742 testChannelPolicy: &testChannelPolicy{ 743 LastUpdate: staleTimestamp, 744 }, 745 }, 746 Capacity: 100000, 747 ChannelID: 5, 748 }, 749 750 // Both edges fresh. 751 symmetricTestChannel("g", "h", 100000, &testChannelPolicy{ 752 LastUpdate: freshTimestamp, 753 }, 6), 754 755 // Both edges stale, only one pruned. This should be pruned for 756 // both normal and strict pruning. 757 symmetricTestChannel("e", "f", 100000, &testChannelPolicy{ 758 LastUpdate: staleTimestamp, 759 }, 7), 760 } 761 762 for _, strictPruning := range []bool{true, false} { 763 // We'll create our test graph and router backed with these test 764 // channels we've created. 765 testGraph, err := createTestGraphFromChannels( 766 t, true, testChannels, "a", 767 ) 768 require.NoError(t, err) 769 770 const startingHeight = 100 771 ctx := createTestCtxFromGraphInstance( 772 t, startingHeight, testGraph, strictPruning, 773 ) 774 775 // All of the channels should exist before pruning them. 776 assertChannelsPruned(t, ctx.graph, testChannels) 777 778 // Proceed to prune the channels - only the last one should be 779 // pruned. 780 require.NoError(t, ctx.builder.pruneZombieChans()) 781 782 // We expect channels that have either both edges stale, or one 783 // edge stale with both known. 784 var prunedChannels []uint64 785 if strictPruning { 786 prunedChannels = []uint64{2, 5, 7} 787 } else { 788 prunedChannels = []uint64{2, 7} 789 } 790 assertChannelsPruned( 791 t, ctx.graph, testChannels, prunedChannels..., 792 ) 793 } 794 } 795 796 // TestPruneChannelGraphDoubleDisabled test that we can properly prune channels 797 // with both edges disabled from our channel graph. 798 func TestPruneChannelGraphDoubleDisabled(t *testing.T) { 799 t.Parallel() 800 801 t.Run("no_assumechannelvalid", func(t *testing.T) { 802 testPruneChannelGraphDoubleDisabled(t, false) 803 }) 804 t.Run("assumechannelvalid", func(t *testing.T) { 805 testPruneChannelGraphDoubleDisabled(t, true) 806 }) 807 } 808 809 func testPruneChannelGraphDoubleDisabled(t *testing.T, assumeValid bool) { 810 timestamp := time.Now() 811 812 // nextTimeStamp is a helper closure that will return a new 813 // timestamp each time it's called, this helps us create channel updates 814 // with new timestamps so that we don't run into our SQL DB constraint 815 // which only allows an update to a channel edge if the last update 816 // timestamp is greater than the previous one. 817 nextTimeStamp := func() time.Time { 818 timestamp = timestamp.Add(time.Second) 819 820 return timestamp 821 } 822 823 // We'll create the following test graph so that only the last channel 824 // is pruned. We'll use a fresh timestamp to ensure they're not pruned 825 // according to that heuristic. 826 testChannels := []*testChannel{ 827 // Channel from self shouldn't be pruned. 828 symmetricTestChannel( 829 "self", "a", 100000, &testChannelPolicy{ 830 LastUpdate: nextTimeStamp(), 831 Disabled: true, 832 }, 99, 833 ), 834 835 // No edges. 836 { 837 Node1: &testChannelEnd{Alias: "a"}, 838 Node2: &testChannelEnd{Alias: "b"}, 839 Capacity: 100000, 840 ChannelID: 1, 841 }, 842 843 // Only one edge disabled. 844 { 845 Node1: &testChannelEnd{ 846 Alias: "a", 847 testChannelPolicy: &testChannelPolicy{ 848 LastUpdate: nextTimeStamp(), 849 Disabled: true, 850 }, 851 }, 852 Node2: &testChannelEnd{Alias: "b"}, 853 Capacity: 100000, 854 ChannelID: 2, 855 }, 856 857 // Only one edge enabled. 858 { 859 Node1: &testChannelEnd{ 860 Alias: "a", 861 testChannelPolicy: &testChannelPolicy{ 862 LastUpdate: nextTimeStamp(), 863 Disabled: false, 864 }, 865 }, 866 Node2: &testChannelEnd{Alias: "b"}, 867 Capacity: 100000, 868 ChannelID: 3, 869 }, 870 871 // One edge disabled, one edge enabled. 872 { 873 Node1: &testChannelEnd{ 874 Alias: "a", 875 testChannelPolicy: &testChannelPolicy{ 876 LastUpdate: nextTimeStamp(), 877 Disabled: true, 878 }, 879 }, 880 Node2: &testChannelEnd{ 881 Alias: "b", 882 testChannelPolicy: &testChannelPolicy{ 883 LastUpdate: nextTimeStamp(), 884 Disabled: false, 885 }, 886 }, 887 Capacity: 100000, 888 ChannelID: 1, 889 }, 890 891 // Both edges enabled. 892 symmetricTestChannel("c", "d", 100000, &testChannelPolicy{ 893 LastUpdate: nextTimeStamp(), 894 Disabled: false, 895 }, 2), 896 897 // Both edges disabled, only one pruned. 898 symmetricTestChannel("e", "f", 100000, &testChannelPolicy{ 899 LastUpdate: nextTimeStamp(), 900 Disabled: true, 901 }, 3), 902 } 903 904 // We'll create our test graph and router backed with these test 905 // channels we've created. 906 testGraph, err := createTestGraphFromChannels( 907 t, true, testChannels, "self", 908 ) 909 require.NoError(t, err, "unable to create test graph") 910 911 const startingHeight = 100 912 ctx := createTestCtxFromGraphInstanceAssumeValid( 913 t, startingHeight, testGraph, assumeValid, false, 914 ) 915 916 // All the channels should exist within the graph before pruning them 917 // when not using AssumeChannelValid, otherwise we should have pruned 918 // the last channel on startup. 919 if !assumeValid { 920 assertChannelsPruned(t, ctx.graph, testChannels) 921 } else { 922 // Sleep to allow the pruning to finish. 923 time.Sleep(200 * time.Millisecond) 924 925 prunedChannel := testChannels[len(testChannels)-1].ChannelID 926 assertChannelsPruned(t, ctx.graph, testChannels, prunedChannel) 927 } 928 929 require.NoError(t, ctx.builder.pruneZombieChans()) 930 931 // If we attempted to prune them without AssumeChannelValid being set, 932 // none should be pruned. Otherwise the last channel should still be 933 // pruned. 934 if !assumeValid { 935 assertChannelsPruned(t, ctx.graph, testChannels) 936 } else { 937 prunedChannel := testChannels[len(testChannels)-1].ChannelID 938 assertChannelsPruned(t, ctx.graph, testChannels, prunedChannel) 939 } 940 } 941 942 // TestIsStaleNode tests that the IsStaleNode method properly detects stale 943 // node announcements. 944 func TestIsStaleNode(t *testing.T) { 945 t.Parallel() 946 ctxb := t.Context() 947 948 const startingBlockHeight = 101 949 ctx := createTestCtxSingleNode(t, startingBlockHeight) 950 951 // Before we can insert a node in to the database, we need to create a 952 // channel that it's linked to. 953 var ( 954 pub1 [33]byte 955 pub2 [33]byte 956 ) 957 copy(pub1[:], priv1.PubKey().SerializeCompressed()) 958 copy(pub2[:], priv2.PubKey().SerializeCompressed()) 959 960 script, fundingTx, _, chanID := createChannelEdge( 961 t, bitcoinKey1.SerializeCompressed(), 962 bitcoinKey2.SerializeCompressed(), 963 10000, 500, 964 ) 965 fundingBlock := &wire.MsgBlock{ 966 Transactions: []*wire.MsgTx{fundingTx}, 967 } 968 ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight) 969 970 edge, err := models.NewV1Channel( 971 chanID.ToUint64(), *chaincfg.SimNetParams.GenesisHash, 972 pub1, pub2, &models.ChannelV1Fields{ 973 BitcoinKey1Bytes: pub1, 974 BitcoinKey2Bytes: pub2, 975 }, models.WithFundingScript(script), 976 ) 977 require.NoError(t, err) 978 979 require.NoError(t, ctx.builder.AddEdge(ctxb, edge)) 980 981 // Before we add the node, if we query for staleness, we should get 982 // false, as we haven't added the full node. 983 updateTimeStamp := time.Unix(123, 0) 984 require.False(t, ctx.builder.IsStaleNode(ctxb, pub1, updateTimeStamp)) 985 986 // With the node stub in the database, we'll add the fully node 987 // announcement to the database. 988 n1 := models.NewV1Node( 989 route.NewVertex(priv1.PubKey()), &models.NodeV1Fields{ 990 LastUpdate: updateTimeStamp, 991 Addresses: testAddrs, 992 Color: color.RGBA{1, 2, 3, 0}, 993 Alias: "node11", 994 AuthSigBytes: testSig.Serialize(), 995 Features: testFeatures.RawFeatureVector, 996 }, 997 ) 998 require.NoError(t, ctx.builder.AddNode(t.Context(), n1)) 999 1000 // If we use the same timestamp and query for staleness, we should get 1001 // true. 1002 require.True(t, ctx.builder.IsStaleNode(ctxb, pub1, updateTimeStamp)) 1003 1004 // If we update the timestamp and once again query for staleness, it 1005 // should report false. 1006 newTimeStamp := time.Unix(1234, 0) 1007 require.False(t, ctx.builder.IsStaleNode(ctxb, pub1, newTimeStamp)) 1008 } 1009 1010 // TestIsKnownEdge tests that the IsKnownEdge method properly detects stale 1011 // channel announcements. 1012 func TestIsKnownEdge(t *testing.T) { 1013 t.Parallel() 1014 ctxb := t.Context() 1015 1016 const startingBlockHeight = 101 1017 ctx := createTestCtxSingleNode(t, startingBlockHeight) 1018 1019 // First, we'll create a new channel edge (just the info) and insert it 1020 // into the database. 1021 var ( 1022 pub1 [33]byte 1023 pub2 [33]byte 1024 ) 1025 copy(pub1[:], priv1.PubKey().SerializeCompressed()) 1026 copy(pub2[:], priv2.PubKey().SerializeCompressed()) 1027 1028 script, fundingTx, _, chanID := createChannelEdge( 1029 t, bitcoinKey1.SerializeCompressed(), 1030 bitcoinKey2.SerializeCompressed(), 1031 10000, 500, 1032 ) 1033 fundingBlock := &wire.MsgBlock{ 1034 Transactions: []*wire.MsgTx{fundingTx}, 1035 } 1036 ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight) 1037 1038 edge, err := models.NewV1Channel( 1039 chanID.ToUint64(), *chaincfg.SimNetParams.GenesisHash, pub1, 1040 pub2, &models.ChannelV1Fields{ 1041 BitcoinKey1Bytes: pub1, 1042 BitcoinKey2Bytes: pub2, 1043 }, 1044 models.WithFundingScript(script), 1045 ) 1046 require.NoError(t, err) 1047 1048 require.NoError(t, ctx.builder.AddEdge(ctxb, edge)) 1049 1050 // Now that the edge has been inserted, query is the router already 1051 // knows of the edge should return true. 1052 require.True(t, ctx.builder.IsKnownEdge(*chanID)) 1053 } 1054 1055 // TestIsStaleEdgePolicy tests that the IsStaleEdgePolicy properly detects 1056 // stale channel edge update announcements. 1057 func TestIsStaleEdgePolicy(t *testing.T) { 1058 t.Parallel() 1059 ctxb := t.Context() 1060 1061 const startingBlockHeight = 101 1062 ctx := createTestCtxFromFile(t, startingBlockHeight, basicGraphFilePath) 1063 1064 // First, we'll create a new channel edge (just the info) and insert it 1065 // into the database. 1066 var ( 1067 pub1 [33]byte 1068 pub2 [33]byte 1069 ) 1070 copy(pub1[:], priv1.PubKey().SerializeCompressed()) 1071 copy(pub2[:], priv2.PubKey().SerializeCompressed()) 1072 1073 script, fundingTx, _, chanID := createChannelEdge( 1074 t, bitcoinKey1.SerializeCompressed(), 1075 bitcoinKey2.SerializeCompressed(), 1076 10000, 500, 1077 ) 1078 fundingBlock := &wire.MsgBlock{ 1079 Transactions: []*wire.MsgTx{fundingTx}, 1080 } 1081 ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight) 1082 1083 // If we query for staleness before adding the edge, we should get 1084 // false. 1085 updateTimeStamp := time.Unix(123, 0) 1086 require.False( 1087 t, ctx.builder.IsStaleEdgePolicy( 1088 *chanID, updateTimeStamp, 0, 1089 ), 1090 ) 1091 require.False( 1092 t, ctx.builder.IsStaleEdgePolicy( 1093 *chanID, updateTimeStamp, 1, 1094 ), 1095 ) 1096 1097 edge, err := models.NewV1Channel( 1098 chanID.ToUint64(), *chaincfg.SimNetParams.GenesisHash, pub1, 1099 pub2, &models.ChannelV1Fields{ 1100 BitcoinKey1Bytes: pub1, 1101 BitcoinKey2Bytes: pub2, 1102 }, models.WithFundingScript(script), 1103 ) 1104 require.NoError(t, err) 1105 1106 require.NoError(t, ctx.builder.AddEdge(ctxb, edge)) 1107 1108 // We'll also add two edge policies, one for each direction. 1109 edgePolicy := &models.ChannelEdgePolicy{ 1110 Version: lnwire.GossipVersion1, 1111 SigBytes: testSig.Serialize(), 1112 ChannelID: edge.ChannelID, 1113 LastUpdate: updateTimeStamp, 1114 TimeLockDelta: 10, 1115 MinHTLC: 1, 1116 FeeBaseMSat: 10, 1117 FeeProportionalMillionths: 10000, 1118 } 1119 edgePolicy.ChannelFlags = 0 1120 require.NoError(t, ctx.builder.UpdateEdge(ctxb, edgePolicy)) 1121 1122 edgePolicy = &models.ChannelEdgePolicy{ 1123 Version: lnwire.GossipVersion1, 1124 SigBytes: testSig.Serialize(), 1125 ChannelID: edge.ChannelID, 1126 LastUpdate: updateTimeStamp, 1127 TimeLockDelta: 10, 1128 MinHTLC: 1, 1129 FeeBaseMSat: 10, 1130 FeeProportionalMillionths: 10000, 1131 } 1132 edgePolicy.ChannelFlags = 1 1133 require.NoError(t, ctx.builder.UpdateEdge(ctxb, edgePolicy)) 1134 1135 // Now that the edges have been added, an identical (chanID, flag, 1136 // timestamp) tuple for each edge should be detected as a stale edge. 1137 require.True( 1138 t, ctx.builder.IsStaleEdgePolicy( 1139 *chanID, updateTimeStamp, 0, 1140 ), 1141 ) 1142 require.True( 1143 t, ctx.builder.IsStaleEdgePolicy( 1144 *chanID, updateTimeStamp, 1, 1145 ), 1146 ) 1147 1148 // If we now update the timestamp for both edges, the router should 1149 // detect that this tuple represents a fresh edge. 1150 updateTimeStamp = time.Unix(9999, 0) 1151 require.False( 1152 t, ctx.builder.IsStaleEdgePolicy( 1153 *chanID, updateTimeStamp, 0, 1154 ), 1155 ) 1156 require.False( 1157 t, ctx.builder.IsStaleEdgePolicy( 1158 *chanID, updateTimeStamp, 1, 1159 ), 1160 ) 1161 } 1162 1163 // TestBlockDifferenceFix tests if when the router is behind on blocks, the 1164 // router catches up to the best block head. 1165 func TestBlockDifferenceFix(t *testing.T) { 1166 t.Parallel() 1167 1168 initialBlockHeight := uint32(0) 1169 1170 // Starting height here is set to 0, which is behind where we want to 1171 // be. 1172 ctx := createTestCtxSingleNode(t, initialBlockHeight) 1173 1174 // Add initial block to our mini blockchain. 1175 block := &wire.MsgBlock{ 1176 Transactions: []*wire.MsgTx{}, 1177 } 1178 ctx.chain.addBlock(block, initialBlockHeight, rand.Uint32()) 1179 1180 // Let's generate a new block of height 5, 5 above where our node is at. 1181 newBlock := &wire.MsgBlock{ 1182 Transactions: []*wire.MsgTx{}, 1183 } 1184 newBlockHeight := uint32(5) 1185 1186 blockDifference := newBlockHeight - initialBlockHeight 1187 1188 ctx.chainView.notifyBlockAck = make(chan struct{}, 1) 1189 1190 ctx.chain.addBlock(newBlock, newBlockHeight, rand.Uint32()) 1191 ctx.chain.setBestBlock(int32(newBlockHeight)) 1192 ctx.chainView.notifyBlock(block.BlockHash(), newBlockHeight, 1193 []*wire.MsgTx{}, t) 1194 1195 <-ctx.chainView.notifyBlockAck 1196 1197 // At this point, the chain notifier should have noticed that we're 1198 // behind on blocks, and will send the n missing blocks that we 1199 // need to the client's epochs channel. Let's replicate this 1200 // functionality. 1201 for i := 0; i < int(blockDifference); i++ { 1202 currBlockHeight := int32(i + 1) 1203 1204 nonce := rand.Uint32() 1205 1206 newBlock := &wire.MsgBlock{ 1207 Transactions: []*wire.MsgTx{}, 1208 Header: wire.BlockHeader{Nonce: nonce}, 1209 } 1210 ctx.chain.addBlock(newBlock, uint32(currBlockHeight), nonce) 1211 currHash := newBlock.Header.BlockHash() 1212 1213 newEpoch := &chainntnfs.BlockEpoch{ 1214 Height: currBlockHeight, 1215 Hash: &currHash, 1216 } 1217 1218 ctx.notifier.EpochChan <- newEpoch 1219 1220 ctx.chainView.notifyBlock(currHash, 1221 uint32(currBlockHeight), block.Transactions, t) 1222 1223 <-ctx.chainView.notifyBlockAck 1224 } 1225 1226 err := wait.NoError(func() error { 1227 // Then router height should be updated to the latest block. 1228 if ctx.builder.bestHeight.Load() != newBlockHeight { 1229 return fmt.Errorf("height should have been updated "+ 1230 "to %v, instead got %v", newBlockHeight, 1231 ctx.builder.bestHeight.Load()) 1232 } 1233 1234 return nil 1235 }, testTimeout) 1236 require.NoError(t, err, "block height wasn't updated") 1237 } 1238 1239 func createTestCtxFromFile(t *testing.T, 1240 startingHeight uint32, testGraph string) *testCtx { 1241 1242 // We'll attempt to locate and parse out the file 1243 // that encodes the graph that our tests should be run against. 1244 graphInstance, err := parseTestGraph(t, true, testGraph) 1245 require.NoError(t, err, "unable to create test graph") 1246 1247 return createTestCtxFromGraphInstance( 1248 t, startingHeight, graphInstance, false, 1249 ) 1250 } 1251 1252 // parseTestGraph returns a fully populated ChannelGraph given a path to a JSON 1253 // file which encodes a test graph. 1254 func parseTestGraph(t *testing.T, useCache bool, path string) ( 1255 *testGraphInstance, error) { 1256 1257 ctx := t.Context() 1258 1259 graphJSON, err := os.ReadFile(path) 1260 if err != nil { 1261 return nil, err 1262 } 1263 1264 // First unmarshal the JSON graph into an instance of the testGraph 1265 // struct. Using the struct tags created above in the struct, the JSON 1266 // will be properly parsed into the struct above. 1267 var g testGraph 1268 if err := json.Unmarshal(graphJSON, &g); err != nil { 1269 return nil, err 1270 } 1271 1272 // We'll use this fake address for the IP address of all the nodes in 1273 // our tests. This value isn't needed for path finding so it doesn't 1274 // need to be unique. 1275 var testAddrs []net.Addr 1276 testAddr, err := net.ResolveTCPAddr("tcp", "192.0.0.1:8888") 1277 if err != nil { 1278 return nil, err 1279 } 1280 testAddrs = append(testAddrs, testAddr) 1281 1282 // Next, create a temporary graph database for usage within the test. 1283 graph := graphdb.MakeTestGraph( 1284 t, graphdb.WithUseGraphCache(useCache), 1285 ) 1286 1287 aliasMap := make(map[string]route.Vertex) 1288 privKeyMap := make(map[string]*btcec.PrivateKey) 1289 channelIDs := make(map[route.Vertex]map[route.Vertex]uint64) 1290 links := make(map[lnwire.ShortChannelID]htlcswitch.ChannelLink) 1291 var source *models.Node 1292 1293 // First we insert all the nodes within the graph as vertexes. 1294 for _, node := range g.Nodes { 1295 pubBytes, err := hex.DecodeString(node.PubKey) 1296 if err != nil { 1297 return nil, err 1298 } 1299 1300 pubKey, err := route.NewVertexFromBytes(pubBytes) 1301 require.NoError(t, err) 1302 1303 dbNode := models.NewV1Node(pubKey, &models.NodeV1Fields{ 1304 AuthSigBytes: testSig.Serialize(), 1305 LastUpdate: testTime, 1306 Addresses: testAddrs, 1307 Alias: node.Alias, 1308 Features: testFeatures.RawFeatureVector, 1309 }) 1310 1311 // We require all aliases within the graph to be unique for our 1312 // tests. 1313 if _, ok := aliasMap[node.Alias]; ok { 1314 return nil, errors.New("aliases for nodes " + 1315 "must be unique!") 1316 } 1317 1318 // If the alias is unique, then add the node to the 1319 // alias map for easy lookup. 1320 aliasMap[node.Alias] = dbNode.PubKeyBytes 1321 1322 // private keys are needed for signing error messages. If set 1323 // check the consistency with the public key. 1324 privBytes, err := hex.DecodeString(node.PrivKey) 1325 if err != nil { 1326 return nil, err 1327 } 1328 if len(privBytes) > 0 { 1329 key, derivedPub := btcec.PrivKeyFromBytes( 1330 privBytes, 1331 ) 1332 1333 if !bytes.Equal( 1334 pubBytes, derivedPub.SerializeCompressed(), 1335 ) { 1336 1337 return nil, fmt.Errorf("%s public key and "+ 1338 "private key are inconsistent\n"+ 1339 "got %x\nwant %x\n", 1340 node.Alias, 1341 derivedPub.SerializeCompressed(), 1342 pubBytes, 1343 ) 1344 } 1345 1346 privKeyMap[node.Alias] = key 1347 } 1348 1349 // If the node is tagged as the source, then we create a 1350 // pointer to is so we can mark the source in the graph 1351 // properly. 1352 if node.Source { 1353 // If we come across a node that's marked as the 1354 // source, and we've already set the source in a prior 1355 // iteration, then the JSON has an error as only ONE 1356 // node can be the source in the graph. 1357 if source != nil { 1358 return nil, errors.New("JSON is invalid " + 1359 "multiple nodes are tagged as the " + 1360 "source") 1361 } 1362 1363 source = dbNode 1364 1365 // Set the selected source node. 1366 if err := graph.SetSourceNode(ctx, source); err != nil { 1367 return nil, err 1368 } 1369 1370 continue 1371 } 1372 1373 // With the node fully parsed, add it as a vertex within the 1374 // graph. 1375 if err := graph.AddNode(ctx, dbNode); err != nil { 1376 return nil, err 1377 } 1378 } 1379 1380 // With all the vertexes inserted, we can now insert the edges into the 1381 // test graph. 1382 for _, edge := range g.Edges { 1383 node1Bytes, err := hex.DecodeString(edge.Node1) 1384 if err != nil { 1385 return nil, err 1386 } 1387 1388 node2Bytes, err := hex.DecodeString(edge.Node2) 1389 if err != nil { 1390 return nil, err 1391 } 1392 1393 if bytes.Compare(node1Bytes, node2Bytes) == 1 { 1394 return nil, fmt.Errorf( 1395 "channel %v node order incorrect", 1396 edge.ChannelID, 1397 ) 1398 } 1399 1400 fundingTXID := strings.Split(edge.ChannelPoint, ":")[0] 1401 txidBytes, err := chainhash.NewHashFromStr(fundingTXID) 1402 if err != nil { 1403 return nil, err 1404 } 1405 fundingPoint := wire.OutPoint{ 1406 Hash: *txidBytes, 1407 Index: 0, 1408 } 1409 1410 // We first insert the existence of the edge between the two 1411 // nodes. 1412 var node1Vertex, node2Vertex route.Vertex 1413 copy(node1Vertex[:], node1Bytes) 1414 copy(node2Vertex[:], node2Bytes) 1415 1416 var btcKey1, btcKey2 route.Vertex 1417 copy(btcKey1[:], node1Bytes) 1418 copy(btcKey2[:], node2Bytes) 1419 1420 edgeInfo, err := models.NewV1Channel( 1421 edge.ChannelID, *chaincfg.SimNetParams.GenesisHash, 1422 node1Vertex, node2Vertex, &models.ChannelV1Fields{ 1423 BitcoinKey1Bytes: btcKey1, 1424 BitcoinKey2Bytes: btcKey2, 1425 }, 1426 models.WithChanProof(&testAuthProof), 1427 models.WithChannelPoint(fundingPoint), 1428 models.WithCapacity(btcutil.Amount(edge.Capacity)), 1429 ) 1430 if err != nil { 1431 return nil, err 1432 } 1433 1434 shortID := lnwire.NewShortChanIDFromInt(edge.ChannelID) 1435 links[shortID] = &mockLink{ 1436 bandwidth: lnwire.MilliSatoshi( 1437 edgeInfo.Capacity * 1000, 1438 ), 1439 } 1440 1441 err = graph.AddChannelEdge(ctx, edgeInfo) 1442 if err != nil && !errors.Is(err, graphdb.ErrEdgeAlreadyExist) { 1443 return nil, err 1444 } 1445 1446 channelFlags := lnwire.ChanUpdateChanFlags(edge.ChannelFlags) 1447 isUpdate1 := channelFlags&lnwire.ChanUpdateDirection == 0 1448 targetNode := edgeInfo.NodeKey1Bytes 1449 if isUpdate1 { 1450 targetNode = edgeInfo.NodeKey2Bytes 1451 } 1452 1453 edgePolicy := &models.ChannelEdgePolicy{ 1454 Version: lnwire.GossipVersion1, 1455 SigBytes: testSig.Serialize(), 1456 MessageFlags: lnwire.ChanUpdateMsgFlags( 1457 edge.MessageFlags, 1458 ), 1459 ChannelFlags: channelFlags, 1460 ChannelID: edge.ChannelID, 1461 LastUpdate: testTime, 1462 TimeLockDelta: edge.Expiry, 1463 MinHTLC: lnwire.MilliSatoshi( 1464 edge.MinHTLC, 1465 ), 1466 MaxHTLC: lnwire.MilliSatoshi( 1467 edge.MaxHTLC, 1468 ), 1469 FeeBaseMSat: lnwire.MilliSatoshi( 1470 edge.FeeBaseMsat, 1471 ), 1472 FeeProportionalMillionths: lnwire.MilliSatoshi( 1473 edge.FeeRate, 1474 ), 1475 ToNode: targetNode, 1476 } 1477 if err := graph.UpdateEdgePolicy(ctx, edgePolicy); err != nil { 1478 return nil, err 1479 } 1480 1481 // We also store the channel IDs info for each of the node. 1482 node1Vertex, err = route.NewVertexFromBytes(node1Bytes) 1483 if err != nil { 1484 return nil, err 1485 } 1486 1487 node2Vertex, err = route.NewVertexFromBytes(node2Bytes) 1488 if err != nil { 1489 return nil, err 1490 } 1491 1492 if _, ok := channelIDs[node1Vertex]; !ok { 1493 channelIDs[node1Vertex] = map[route.Vertex]uint64{} 1494 } 1495 channelIDs[node1Vertex][node2Vertex] = edge.ChannelID 1496 1497 if _, ok := channelIDs[node2Vertex]; !ok { 1498 channelIDs[node2Vertex] = map[route.Vertex]uint64{} 1499 } 1500 channelIDs[node2Vertex][node1Vertex] = edge.ChannelID 1501 } 1502 1503 return &testGraphInstance{ 1504 graph: graphdb.NewVersionedGraph( 1505 graph, lnwire.GossipVersion1, 1506 ), 1507 aliasMap: aliasMap, 1508 privKeyMap: privKeyMap, 1509 channelIDs: channelIDs, 1510 links: links, 1511 }, nil 1512 } 1513 1514 // testGraph is the struct which corresponds to the JSON format used to encode 1515 // graphs within the files in the testdata directory. 1516 // 1517 // TODO(roasbeef): add test graph auto-generator. 1518 type testGraph struct { 1519 Info []string `json:"info"` 1520 Nodes []testNode `json:"nodes"` 1521 Edges []testChan `json:"edges"` 1522 } 1523 1524 // testNode represents a node within the test graph above. We skip certain 1525 // information such as the node's IP address as that information isn't needed 1526 // for our tests. Private keys are optional. If set, they should be consistent 1527 // with the public key. The private key is used to sign error messages 1528 // sent from the node. 1529 type testNode struct { 1530 Source bool `json:"source"` 1531 PubKey string `json:"pubkey"` 1532 PrivKey string `json:"privkey"` 1533 Alias string `json:"alias"` 1534 } 1535 1536 // testChan represents the JSON version of a payment channel. This struct 1537 // matches the Json that's encoded under the "edges" key within the test graph. 1538 type testChan struct { 1539 Node1 string `json:"node_1"` 1540 Node2 string `json:"node_2"` 1541 ChannelID uint64 `json:"channel_id"` 1542 ChannelPoint string `json:"channel_point"` 1543 ChannelFlags uint8 `json:"channel_flags"` 1544 MessageFlags uint8 `json:"message_flags"` 1545 Expiry uint16 `json:"expiry"` 1546 MinHTLC int64 `json:"min_htlc"` 1547 MaxHTLC int64 `json:"max_htlc"` 1548 FeeBaseMsat int64 `json:"fee_base_msat"` 1549 FeeRate int64 `json:"fee_rate"` 1550 Capacity int64 `json:"capacity"` 1551 } 1552 1553 type testChannel struct { 1554 Node1 *testChannelEnd 1555 Node2 *testChannelEnd 1556 Capacity btcutil.Amount 1557 ChannelID uint64 1558 } 1559 1560 type testChannelEnd struct { 1561 Alias string 1562 *testChannelPolicy 1563 } 1564 1565 func symmetricTestChannel(alias1, alias2 string, capacity btcutil.Amount, 1566 policy *testChannelPolicy, chanID ...uint64) *testChannel { 1567 1568 // Leaving id zero will result in auto-generation of a channel id during 1569 // graph construction. 1570 var id uint64 1571 if len(chanID) > 0 { 1572 id = chanID[0] 1573 } 1574 1575 policy2 := *policy 1576 1577 return asymmetricTestChannel( 1578 alias1, alias2, capacity, policy, &policy2, id, 1579 ) 1580 } 1581 1582 func asymmetricTestChannel(alias1, alias2 string, capacity btcutil.Amount, 1583 policy1, policy2 *testChannelPolicy, id uint64) *testChannel { 1584 1585 return &testChannel{ 1586 Capacity: capacity, 1587 Node1: &testChannelEnd{ 1588 Alias: alias1, 1589 testChannelPolicy: policy1, 1590 }, 1591 Node2: &testChannelEnd{ 1592 Alias: alias2, 1593 testChannelPolicy: policy2, 1594 }, 1595 ChannelID: id, 1596 } 1597 } 1598 1599 // assertChannelsPruned ensures that only the given channels are pruned from the 1600 // graph out of the set of all channels. 1601 func assertChannelsPruned(t *testing.T, graph *graphdb.VersionedGraph, 1602 channels []*testChannel, prunedChanIDs ...uint64) { 1603 1604 t.Helper() 1605 1606 pruned := make(map[uint64]struct{}, len(channels)) 1607 for _, chanID := range prunedChanIDs { 1608 pruned[chanID] = struct{}{} 1609 } 1610 1611 for _, channel := range channels { 1612 _, shouldPrune := pruned[channel.ChannelID] 1613 exists, isZombie, err := graph.HasChannelEdge( 1614 t.Context(), channel.ChannelID, 1615 ) 1616 require.NoError(t, err) 1617 if shouldPrune { 1618 require.Falsef(t, exists, 1619 "expected channel=%v to not exist within "+ 1620 "the graph", 1621 channel.ChannelID) 1622 require.Truef(t, isZombie, 1623 "expected channel=%v to be marked as zombie", 1624 channel.ChannelID) 1625 1626 continue 1627 } 1628 1629 require.Truef(t, exists, 1630 "expected channel=%v to exist within the graph", 1631 channel.ChannelID) 1632 require.Falsef(t, isZombie, 1633 "expected channel=%v to not be marked as zombie", 1634 channel.ChannelID) 1635 } 1636 } 1637 1638 type testChannelPolicy struct { 1639 Expiry uint16 1640 MinHTLC lnwire.MilliSatoshi 1641 MaxHTLC lnwire.MilliSatoshi 1642 FeeBaseMsat lnwire.MilliSatoshi 1643 FeeRate lnwire.MilliSatoshi 1644 InboundFeeBaseMsat int64 1645 InboundFeeRate int64 1646 LastUpdate time.Time 1647 Disabled bool 1648 Features *lnwire.FeatureVector 1649 } 1650 1651 // createTestGraphFromChannels returns a fully populated ChannelGraph based on a 1652 // set of test channels. Additional required information like keys are derived 1653 // in a deterministic way and added to the channel graph. A list of nodes is not 1654 // required and derived from the channel data. The goal is to keep instantiating 1655 // a test channel graph as light weight as possible. 1656 func createTestGraphFromChannels(t *testing.T, useCache bool, 1657 testChannels []*testChannel, source string) (*testGraphInstance, 1658 error) { 1659 1660 ctx := t.Context() 1661 1662 // We'll use this fake address for the IP address of all the nodes in 1663 // our tests. This value isn't needed for path finding so it doesn't 1664 // need to be unique. 1665 var testAddrs []net.Addr 1666 testAddr, err := net.ResolveTCPAddr("tcp", "192.0.0.1:8888") 1667 if err != nil { 1668 return nil, err 1669 } 1670 testAddrs = append(testAddrs, testAddr) 1671 1672 // Next, create a temporary graph database for usage within the test. 1673 graph := graphdb.MakeTestGraph( 1674 t, graphdb.WithUseGraphCache(useCache), 1675 ) 1676 1677 aliasMap := make(map[string]route.Vertex) 1678 privKeyMap := make(map[string]*btcec.PrivateKey) 1679 1680 nodeIndex := byte(0) 1681 addNodeWithAlias := func(alias string, 1682 features *lnwire.FeatureVector) error { 1683 1684 keyBytes := []byte{ 1685 0, 0, 0, 0, 0, 0, 0, 0, 1686 0, 0, 0, 0, 0, 0, 0, 0, 1687 0, 0, 0, 0, 0, 0, 0, 0, 1688 0, 0, 0, 0, 0, 0, 0, nodeIndex + 1, 1689 } 1690 1691 privKey, pubKey := btcec.PrivKeyFromBytes(keyBytes) 1692 1693 if features == nil { 1694 features = lnwire.EmptyFeatureVector() 1695 } 1696 1697 dbNode := models.NewV1Node( 1698 route.NewVertex(pubKey), &models.NodeV1Fields{ 1699 AuthSigBytes: testSig.Serialize(), 1700 LastUpdate: testTime, 1701 Addresses: testAddrs, 1702 Alias: alias, 1703 Features: features.RawFeatureVector, 1704 }, 1705 ) 1706 1707 privKeyMap[alias] = privKey 1708 1709 // With the node fully parsed, add it as a vertex within the 1710 // graph. 1711 if alias == source { 1712 err = graph.SetSourceNode(ctx, dbNode) 1713 require.NoError(t, err) 1714 } else { 1715 err := graph.AddNode(ctx, dbNode) 1716 require.NoError(t, err) 1717 } 1718 1719 aliasMap[alias] = dbNode.PubKeyBytes 1720 nodeIndex++ 1721 1722 return nil 1723 } 1724 1725 // Add the source node. 1726 err = addNodeWithAlias(source, lnwire.EmptyFeatureVector()) 1727 if err != nil { 1728 return nil, err 1729 } 1730 1731 // Initialize variable that keeps track of the next channel id to assign 1732 // if none is specified. 1733 nextUnassignedChannelID := uint64(100000) 1734 1735 links := make(map[lnwire.ShortChannelID]htlcswitch.ChannelLink) 1736 1737 for _, testChannel := range testChannels { 1738 for _, node := range []*testChannelEnd{ 1739 testChannel.Node1, testChannel.Node2, 1740 } { 1741 _, exists := aliasMap[node.Alias] 1742 if !exists { 1743 var features *lnwire.FeatureVector 1744 if node.testChannelPolicy != nil { 1745 features = 1746 node.testChannelPolicy.Features 1747 } 1748 err := addNodeWithAlias( 1749 node.Alias, features, 1750 ) 1751 if err != nil { 1752 return nil, err 1753 } 1754 } 1755 } 1756 1757 channelID := testChannel.ChannelID 1758 1759 // If no channel id is specified, generate an id. 1760 if channelID == 0 { 1761 channelID = nextUnassignedChannelID 1762 nextUnassignedChannelID++ 1763 } 1764 1765 var hash [sha256.Size]byte 1766 hash[len(hash)-1] = byte(channelID) 1767 1768 fundingPoint := &wire.OutPoint{ 1769 Hash: chainhash.Hash(hash), 1770 Index: 0, 1771 } 1772 1773 capacity := lnwire.MilliSatoshi(testChannel.Capacity * 1000) 1774 shortID := lnwire.NewShortChanIDFromInt(channelID) 1775 links[shortID] = &mockLink{ 1776 bandwidth: capacity, 1777 } 1778 1779 // Sort nodes 1780 node1 := testChannel.Node1 1781 node2 := testChannel.Node2 1782 node1Vertex := aliasMap[node1.Alias] 1783 node2Vertex := aliasMap[node2.Alias] 1784 if bytes.Compare(node1Vertex[:], node2Vertex[:]) == 1 { 1785 node1, node2 = node2, node1 1786 node1Vertex, node2Vertex = node2Vertex, node1Vertex 1787 } 1788 1789 // We first insert the existence of the edge between the two 1790 // nodes. 1791 edgeInfo, err := models.NewV1Channel( 1792 channelID, *chaincfg.SimNetParams.GenesisHash, 1793 node1Vertex, node2Vertex, &models.ChannelV1Fields{ 1794 BitcoinKey1Bytes: node1Vertex, 1795 BitcoinKey2Bytes: node2Vertex, 1796 }, 1797 models.WithChanProof(&testAuthProof), 1798 models.WithChannelPoint(*fundingPoint), 1799 models.WithCapacity(testChannel.Capacity), 1800 ) 1801 if err != nil { 1802 return nil, err 1803 } 1804 1805 err = graph.AddChannelEdge(ctx, edgeInfo) 1806 if err != nil && 1807 !errors.Is(err, graphdb.ErrEdgeAlreadyExist) { 1808 1809 return nil, err 1810 } 1811 1812 getExtraData := func( 1813 end *testChannelEnd) lnwire.ExtraOpaqueData { 1814 1815 var extraData lnwire.ExtraOpaqueData 1816 inboundFee := lnwire.Fee{ 1817 BaseFee: int32(end.InboundFeeBaseMsat), 1818 FeeRate: int32(end.InboundFeeRate), 1819 } 1820 require.NoError(t, extraData.PackRecords(&inboundFee)) 1821 1822 return extraData 1823 } 1824 1825 if node1.testChannelPolicy != nil { 1826 var msgFlags lnwire.ChanUpdateMsgFlags 1827 if node1.MaxHTLC != 0 { 1828 msgFlags |= lnwire.ChanUpdateRequiredMaxHtlc 1829 } 1830 var channelFlags lnwire.ChanUpdateChanFlags 1831 if node1.Disabled { 1832 channelFlags |= lnwire.ChanUpdateDisabled 1833 } 1834 1835 //nolint:ll 1836 edgePolicy := &models.ChannelEdgePolicy{ 1837 Version: lnwire.GossipVersion1, 1838 SigBytes: testSig.Serialize(), 1839 MessageFlags: msgFlags, 1840 ChannelFlags: channelFlags, 1841 ChannelID: channelID, 1842 LastUpdate: node1.LastUpdate, 1843 TimeLockDelta: node1.Expiry, 1844 MinHTLC: node1.MinHTLC, 1845 MaxHTLC: node1.MaxHTLC, 1846 FeeBaseMSat: node1.FeeBaseMsat, 1847 FeeProportionalMillionths: node1.FeeRate, 1848 ToNode: node2Vertex, 1849 ExtraOpaqueData: getExtraData(node1), 1850 } 1851 err := graph.UpdateEdgePolicy(ctx, edgePolicy) 1852 if err != nil { 1853 return nil, err 1854 } 1855 } 1856 1857 if node2.testChannelPolicy != nil { 1858 var msgFlags lnwire.ChanUpdateMsgFlags 1859 if node2.MaxHTLC != 0 { 1860 msgFlags |= lnwire.ChanUpdateRequiredMaxHtlc 1861 } 1862 var channelFlags lnwire.ChanUpdateChanFlags 1863 if node2.Disabled { 1864 channelFlags |= lnwire.ChanUpdateDisabled 1865 } 1866 channelFlags |= lnwire.ChanUpdateDirection 1867 1868 //nolint:ll 1869 edgePolicy := &models.ChannelEdgePolicy{ 1870 Version: lnwire.GossipVersion1, 1871 SigBytes: testSig.Serialize(), 1872 MessageFlags: msgFlags, 1873 ChannelFlags: channelFlags, 1874 ChannelID: channelID, 1875 LastUpdate: node2.LastUpdate, 1876 TimeLockDelta: node2.Expiry, 1877 MinHTLC: node2.MinHTLC, 1878 MaxHTLC: node2.MaxHTLC, 1879 FeeBaseMSat: node2.FeeBaseMsat, 1880 FeeProportionalMillionths: node2.FeeRate, 1881 ToNode: node1Vertex, 1882 ExtraOpaqueData: getExtraData(node2), 1883 } 1884 err := graph.UpdateEdgePolicy(ctx, edgePolicy) 1885 if err != nil { 1886 return nil, err 1887 } 1888 } 1889 1890 channelID++ //nolint:ineffassign 1891 } 1892 1893 return &testGraphInstance{ 1894 graph: graphdb.NewVersionedGraph( 1895 graph, lnwire.GossipVersion1, 1896 ), 1897 aliasMap: aliasMap, 1898 privKeyMap: privKeyMap, 1899 links: links, 1900 }, nil 1901 } 1902 1903 type mockLink struct { 1904 htlcswitch.ChannelLink 1905 bandwidth lnwire.MilliSatoshi 1906 mayAddOutgoingErr error 1907 ineligible bool 1908 } 1909 1910 // Bandwidth returns the bandwidth the mock was configured with. 1911 func (m *mockLink) Bandwidth() lnwire.MilliSatoshi { 1912 return m.bandwidth 1913 } 1914 1915 // EligibleToForward returns the mock's configured eligibility. 1916 func (m *mockLink) EligibleToForward() bool { 1917 return !m.ineligible 1918 } 1919 1920 // MayAddOutgoingHtlc returns the error configured in our mock. 1921 func (m *mockLink) MayAddOutgoingHtlc(_ lnwire.MilliSatoshi) error { 1922 return m.mayAddOutgoingErr 1923 }