import { ShortChannelId } from "@node-lightning/core" ; import { expect , use } from "chai" ; import { GossipRelay } from "../../lib/gossip/GossipRelay" ; import { GossipRelayState } from "../../lib/gossip/GossipRelay" ; import { ChannelAnnouncementMessage } from "../../lib/messages/ChannelAnnouncementMessage" ; import { ChannelUpdateMessage } from "../../lib/messages/ChannelUpdateMessage" ; import { NodeAnnouncementMessage } from "../../lib/messages/NodeAnnouncementMessage" ; import { createFakeLogger , createFakePeer , wait } from "../_test-utils" ; describe ( "GossipRelay" , ( ) => { let sut ; function createChanAnn ( block , tx , output ) { const msg = new ChannelAnnouncementMessage ( ) ; msg . shortChannelId = new ShortChannelId ( block , tx , output ) ; return msg ; } function createChanUpd ( block , tx , output , timestamp ) { const msg = new ChannelUpdateMessage ( ) ; msg . shortChannelId = new ShortChannelId ( block , tx , output ) ; msg . timestamp = timestamp ; return msg ; } function createNodeAnn ( nodeIdHex , timestamp ) { const msg = new NodeAnnouncementMessage ( ) ; msg . nodeId = Buffer . from ( nodeIdHex , "hex" ) ; msg . timestamp = timestamp ; return msg ; } beforeEach ( ( ) => { sut = new GossipRelay ( createFakeLogger ( ) , 100 , 3 ) ; } ) ; describe ( "state: Inactive" , ( ) => { describe ( "on .start()" , ( ) => { beforeEach ( ( ) => { sut . start ( ) ; } ) ; afterEach ( ( ) => { sut . stop ( ) ; } ) ; it ( "starts periodic flushing" , async ( ) => { const peer1 = createFakePeer ( ) ; sut . addPeer ( peer1 ) ; const msg1 = createChanAnn ( 0 , 0 , 0 ) ; sut . enqueue ( msg1 ) ; await wait ( 200 ) ; expect ( peer1 . sendMessage . called ) . to . be . true ; expect ( peer1 . sendMessage . args [ 0 ] [ 0 ] ) . to . equal ( msg1 ) ; } ) ; it ( "transitions state to Active" , ( ) => { sut . start ( ) ; expect ( sut . state ) . to . equal ( GossipRelayState . Active ) ; } ) ; } ) ; describe ( "on .stop()" , ( ) => { it ( "no-op" , ( ) => { sut . stop ( ) ; } ) ; } ) ; describe ( "on enqueue" , ( ) => { it ( "ignores message" , ( ) => { sut . enqueue ( createChanAnn ( 0 , 0 , 0 ) ) ; expect ( ( sut as any ) . _queue . length ) . to . equal ( 0 ) ; } ) ; } ) ; } ) ; describe ( "state: Active" , ( ) => { beforeEach ( ( ) => { sut . start ( ) ; } ) ; afterEach ( ( ) => { sut . stop ( ) ; } ) ; describe ( "on .start()" , ( ) => { it ( "no-op" , ( ) => { sut . start ( ) ; } ) ; } ) ; describe ( "on .stop()" , ( ) => { it ( "stops flushing" , async ( ) => { const peer1 = createFakePeer ( ) ; sut . addPeer ( peer1 ) ; const msg1 = createChanAnn ( 0 , 0 , 0 ) ; sut . enqueue ( msg1 ) ; await wait ( 50 ) ; sut . stop ( ) ; await wait ( 200 ) ; expect ( peer1 . sendMessage . called ) . to . be . false ; } ) ; it ( "changes state to Inactive" , ( ) => { expect ( sut . state ) . to . equal ( GossipRelayState . Active ) ; sut . stop ( ) ; expect ( sut . state ) . to . equal ( GossipRelayState . Inactive ) ; } ) ; } ) ; describe ( "on enqueue" , ( ) => { describe ( "channel_announcement" , ( ) => { it ( "adds new message" , ( ) => { sut . enqueue ( createChanAnn ( 0 , 0 , 0 ) ) ; expect ( ( sut as any ) . _queue . length ) . to . equal ( 1 ) ; } ) ; it ( "ignores duplicate" , ( ) => { sut . enqueue ( createChanAnn ( 0 , 0 , 0 ) ) ; sut . enqueue ( createChanAnn ( 0 , 1 , 0 ) ) ; expect ( ( sut as any ) . _queue . length ) . to . equal ( 2 ) ; sut . enqueue ( createChanAnn ( 0 , 0 , 0 ) ) ; expect ( ( sut as any ) . _queue . length ) . to . equal ( 2 , "should ignore duplicate" ) ; expect ( ( sut as any ) . _queue [ 0 ] . shortChannelId . txIdx ) . to . equal ( 0 , "duplicate should maintain original position" ) ; } ) ; } ) ; describe ( "channel_update" , ( ) => { it ( "adds new message" , ( ) => { sut . enqueue ( createChanUpd ( 0 , 0 , 0 , 0 ) ) ; expect ( ( sut as any ) . _queue . length ) . to . equal ( 1 ) ; } ) ; it ( "ignores duplicate" , ( ) => { sut . enqueue ( createChanUpd ( 0 , 0 , 0 , 0 ) ) ; sut . enqueue ( createChanUpd ( 0 , 1 , 0 , 0 ) ) ; expect ( ( sut as any ) . _queue . length ) . to . equal ( 2 ) ; sut . enqueue ( createChanUpd ( 0 , 0 , 0 , 0 ) ) ; expect ( ( sut as any ) . _queue . length ) . to . equal ( 2 , "should ignore update" ) ; expect ( ( sut as any ) . _queue [ 0 ] . shortChannelId . txIdx ) . to . equal ( 0 , "duplicate should maintain original position" ) ; } ) ; it ( "replaces outdated" , ( ) => { sut . enqueue ( createChanUpd ( 0 , 0 , 0 , 0 ) ) ; sut . enqueue ( createChanUpd ( 1 , 0 , 0 , 0 ) ) ; expect ( ( sut as any ) . _queue . length ) . to . equal ( 2 ) ; sut . enqueue ( createChanUpd ( 0 , 0 , 0 , 1 ) ) ; expect ( ( sut as any ) . _queue [ 0 ] . shortChannelId . block ) . to . equal ( 1 ) ; expect ( ( sut as any ) . _queue [ 1 ] . shortChannelId . block ) . to . equal ( 0 ) ; } ) ; } ) ; describe ( "node_announcement" , ( ) => { it ( "adds new message" , ( ) => { sut . enqueue ( createNodeAnn ( "00" . repeat ( 32 ) , 0 ) ) ; expect ( ( sut as any ) . _queue . length ) . to . equal ( 1 ) ; } ) ; it ( "ignores duplicate" , ( ) => { sut . enqueue ( createNodeAnn ( "00" . repeat ( 32 ) , 0 ) ) ; sut . enqueue ( createNodeAnn ( "01" . repeat ( 32 ) , 0 ) ) ; expect ( ( sut as any ) . _queue . length ) . to . equal ( 2 ) ; sut . enqueue ( createNodeAnn ( "00" . repeat ( 32 ) , 0 ) ) ; expect ( ( sut as any ) . _queue [ 0 ] . nodeId . toString ( "hex" ) ) . to . equal ( "00" . repeat ( 32 ) ) ; expect ( ( sut as any ) . _queue [ 1 ] . nodeId . toString ( "hex" ) ) . to . equal ( "01" . repeat ( 32 ) ) ; } ) ; it ( "replaces outdated" , ( ) => { sut . enqueue ( createNodeAnn ( "00" . repeat ( 32 ) , 0 ) ) ; sut . enqueue ( createNodeAnn ( "01" . repeat ( 32 ) , 0 ) ) ; expect ( ( sut as any ) . _queue . length ) . to . equal ( 2 ) ; sut . enqueue ( createNodeAnn ( "00" . repeat ( 32 ) , 1 ) ) ; expect ( ( sut as any ) . _queue [ 0 ] . nodeId . toString ( "hex" ) ) . to . equal ( "01" . repeat ( 32 ) ) ; expect ( ( sut as any ) . _queue [ 1 ] . nodeId . toString ( "hex" ) ) . to . equal ( "00" . repeat ( 32 ) ) ; expect ( ( sut as any ) . _queue [ 1 ] . timestamp ) . to . equal ( 1 ) ; } ) ; } ) ; } ) ; describe ( "on timer" , ( ) => { it ( "flushes to single message peer" , ( ) => { const peer1 = createFakePeer ( ) ; sut . addPeer ( peer1 ) ; const msg1 = createChanAnn ( 0 , 0 , 0 ) ; sut . enqueue ( msg1 ) ; ( sut as any ) . _onTimer ( ) ; expect ( peer1 . sendMessage . called ) . to . be . true ; expect ( peer1 . sendMessage . args [ 0 ] [ 0 ] ) . to . equal ( msg1 ) ; } ) ; it ( "flushes to several messages peer" , ( ) => { const peer1 = createFakePeer ( ) ; sut . addPeer ( peer1 ) ; const msg1 = createChanAnn ( 0 , 0 , 0 ) ; const msg2 = createChanAnn ( 1 , 0 , 0 ) ; sut . enqueue ( msg1 ) ; sut . enqueue ( msg2 ) ; ( sut as any ) . _onTimer ( ) ; expect ( peer1 . sendMessage . called ) . to . be . true ; expect ( peer1 . sendMessage . args [ 0 ] [ 0 ] ) . to . equal ( msg1 ) ; expect ( peer1 . sendMessage . args [ 1 ] [ 0 ] ) . to . equal ( msg2 ) ; } ) ; it ( "flushes new messages to peer" , ( ) => { const peer1 = createFakePeer ( ) ; sut . addPeer ( peer1 ) ; const msg1 = createChanAnn ( 0 , 0 , 0 ) ; sut . enqueue ( msg1 ) ; ( sut as any ) . _onTimer ( ) ; peer1 . sendMessage . reset ( ) ; const msg2 = createChanAnn ( 1 , 0 , 0 ) ; sut . enqueue ( msg2 ) ; ( sut as any ) . _onTimer ( ) ; expect ( peer1 . sendMessage . called ) . to . be . true ; expect ( peer1 . sendMessage . args [ 0 ] [ 0 ] ) . to . equal ( msg2 ) ; } ) ; it ( "flushes new messages to peers" , ( ) => { const peer1 = createFakePeer ( ) ; sut . addPeer ( peer1 ) ; const msg1 = createChanAnn ( 0 , 0 , 0 ) ; sut . enqueue ( msg1 ) ; ( sut as any ) . _onTimer ( ) ; peer1 . sendMessage . reset ( ) ; const peer2 = createFakePeer ( ) ; sut . addPeer ( peer2 ) ; const msg2 = createChanAnn ( 1 , 0 , 0 ) ; sut . enqueue ( msg2 ) ; ( sut as any ) . _onTimer ( ) ; expect ( peer1 . sendMessage . called ) . to . be . true ; expect ( peer1 . sendMessage . args [ 0 ] [ 0 ] ) . to . equal ( msg2 ) ; expect ( peer2 . sendMessage . called ) . to . be . true ; expect ( peer2 . sendMessage . args [ 0 ] [ 0 ] ) . to . equal ( msg2 ) ; } ) ; it ( "prunes excess messages" , ( ) => { sut . enqueue ( createChanAnn ( 0 , 0 , 0 ) ) ; sut . enqueue ( createChanAnn ( 1 , 0 , 0 ) ) ; sut . enqueue ( createChanAnn ( 2 , 0 , 0 ) ) ; sut . enqueue ( createChanAnn ( 3 , 0 , 0 ) ) ; sut . enqueue ( createChanAnn ( 4 , 0 , 0 ) ) ; ( sut as any ) . _onTimer ( ) ; expect ( ( sut as any ) . _queue . length ) . to . equal ( 3 ) ; expect ( ( sut as any ) . _queue [ 0 ] . shortChannelId . block ) . to . equal ( 2 ) ; } ) ; it ( "it adjusts peer positions based on index count" , ( ) => { const peer1 = createFakePeer ( ) ; sut . addPeer ( peer1 ) ; sut . enqueue ( createChanAnn ( 0 , 0 , 0 ) ) ; sut . enqueue ( createChanAnn ( 1 , 0 , 0 ) ) ; sut . enqueue ( createChanAnn ( 2 , 0 , 0 ) ) ; ( sut as any ) . _onTimer ( ) ; sut . enqueue ( createChanAnn ( 3 , 0 , 0 ) ) ; sut . enqueue ( createChanAnn ( 4 , 0 , 0 ) ) ; ( sut as any ) . _onTimer ( ) ; expect ( ( sut as any ) . _peers . get ( peer1 ) ) . to . equal ( 3 ) ; } ) ; } ) ; } ) ; describe ( "add peer" , ( ) => { it ( "adds peer at current depth" , ( ) => { const peer1 = createFakePeer ( ) ; sut . addPeer ( peer1 ) ; expect ( ( sut as any ) . _peers . size ) . to . equal ( 1 ) ; } ) ; } ) ; describe ( "remove peer" , ( ) => { it ( "removes peer from list" , ( ) => { const peer1 = createFakePeer ( ) ; sut . addPeer ( peer1 ) ; sut . removePeer ( peer1 ) ; expect ( ( sut as any ) . _peers . size ) . to . equal ( 0 ) ; } ) ; } ) ; } ) ;