UNPKG

@node-lightning/wire

Version:
358 lines (287 loc) 13.3 kB
// tslint:disable: no-unused-expression import { ShortChannelId } from "@node-lightning/core"; import { expect } 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: GossipRelay; function createChanAnn(block: number, tx: number, output: number): ChannelAnnouncementMessage { const msg = new ChannelAnnouncementMessage(); msg.shortChannelId = new ShortChannelId(block, tx, output); return msg; } function createChanUpd( block: number, tx: number, output: number, timestamp: number, ): ChannelUpdateMessage { const msg = new ChannelUpdateMessage(); msg.shortChannelId = new ShortChannelId(block, tx, output); msg.timestamp = timestamp; return msg; } function createNodeAnn(nodeIdHex: string, timestamp: number): NodeAnnouncementMessage { 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 () => { // create a peer const peer1 = createFakePeer(); sut.addPeer(peer1); // enqueue a message that normally would go to a peer const msg1 = createChanAnn(0, 0, 0); sut.enqueue(msg1); // stop activity before the timer would tick await wait(50); sut.stop(); // wait until after the timer would normally have ticked await wait(200); // ensure no message was sent 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"); // prettier-ignore }); }); 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"); // prettier-ignore }); 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", () => { // create a fake peer and add const peer1 = createFakePeer(); sut.addPeer(peer1); // push a new message const msg1 = createChanAnn(0, 0, 0); sut.enqueue(msg1); // manually trigger the timer for testing purposes (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", () => { // create a fake peer and add const peer1 = createFakePeer(); sut.addPeer(peer1); // push messages const msg1 = createChanAnn(0, 0, 0); const msg2 = createChanAnn(1, 0, 0); sut.enqueue(msg1); sut.enqueue(msg2); // manually trigger the timer for testing purposes (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", () => { // create a fake peer and add const peer1 = createFakePeer(); sut.addPeer(peer1); // push messages const msg1 = createChanAnn(0, 0, 0); sut.enqueue(msg1); // manually trigger the timer for testing purposes (sut as any)._onTimer(); // reset the stub peer1.sendMessage.reset(); // push new message const msg2 = createChanAnn(1, 0, 0); sut.enqueue(msg2); // manually trigger the timer for testing purposes (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", () => { // create a fake peer and add const peer1 = createFakePeer(); sut.addPeer(peer1); // push messages const msg1 = createChanAnn(0, 0, 0); sut.enqueue(msg1); // manually trigger the timer for testing purposes (sut as any)._onTimer(); // reset the stub peer1.sendMessage.reset(); // add a new peer const peer2 = createFakePeer(); sut.addPeer(peer2); // push new message const msg2 = createChanAnn(1, 0, 0); sut.enqueue(msg2); // manually trigger the timer for testing purposes (sut as any)._onTimer(); // peer1 should only see the 2nd message expect(peer1.sendMessage.called).to.be.true; expect(peer1.sendMessage.args[0][0]).to.equal(msg2); // peer2 should only see the 2nd message 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)); // manually trigger the timer for testing purposes (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", () => { // create a fake peer and add const peer1 = createFakePeer(); sut.addPeer(peer1); // enqueue some items - peer is at index 0 sut.enqueue(createChanAnn(0, 0, 0)); // index 0 sut.enqueue(createChanAnn(1, 0, 0)); // index 1 sut.enqueue(createChanAnn(2, 0, 0)); // index 2 // manually trigger the timer for testing purposes (sut as any)._onTimer(); // enqueue more items - peer is at index 3 sut.enqueue(createChanAnn(3, 0, 0)); // index 3 sut.enqueue(createChanAnn(4, 0, 0)); // index 4 // manually trigger the timer for testing purposes which // will cause a prune of and leave 3 items in the queue (sut as any)._onTimer(); expect((sut as any)._peers.get(peer1)).to.equal(3); }); }); }); describe("add peer", () => { it("adds peer at current depth", () => { // create a fake peer and add const peer1 = createFakePeer(); sut.addPeer(peer1); expect((sut as any)._peers.size).to.equal(1); }); }); describe("remove peer", () => { it("removes peer from list", () => { // create a fake peer and add const peer1 = createFakePeer(); sut.addPeer(peer1); sut.removePeer(peer1); expect((sut as any)._peers.size).to.equal(0); }); }); });