Skip to content
This repository was archived by the owner on Dec 19, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions packages/wire/__tests__/messages/ShutdownChannelMessage.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ChannelId } from "@node-lightning/core";
import { expect } from "chai";
import { ShutdownMessage } from "../../lib/messages/ShutdownMessage";

describe("ShutdownChannelMessage", () => {
describe(".deserialize", () => {
it("should deserialize without error", () => {
const input = Buffer.from(
"0026"+ // type
"0000000000000000000000000000000000000000000000000000000000000000" + // Channel ID
"0015" + //len
"00a41a8527eab06efc0a8df57045d247784a071e23" //p2wpkh
,"hex"); // prettier-ignore
const result = ShutdownMessage.deserialize(input);
expect(result.type).to.equal(38);
expect(result.channelId.toString()).to.equal("0000000000000000000000000000000000000000000000000000000000000000"); // prettier-ignore
expect(result.scriptPubKey.toString("hex")).to.equal("00a41a8527eab06efc0a8df57045d247784a071e23"); // prettier-ignore
});
});
describe(".serialize", () => {
it("should serialize a message", () => {
const instance = new ShutdownMessage();
instance.channelId = new ChannelId(Buffer.from("0000000000000000000000000000000000000000000000000000000000000000", "hex")); // prettier-ignore
instance.scriptPubKey = Buffer.from("00a41a8527eab06efc0a8df57045d247784a071e23", "hex"); // prettier-ignore

const result = instance.serialize();
expect(result.toString("hex")).to.equal(
"0026" +
"0000000000000000000000000000000000000000000000000000000000000000" +
"0015" +
"00a41a8527eab06efc0a8df57045d247784a071e23",
);
});
});
});
3 changes: 3 additions & 0 deletions packages/wire/lib/MessageFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { QueryChannelRangeMessage } from "./messages/QueryChannelRangeMessage";
import { QueryShortChannelIdsMessage } from "./messages/QueryShortChannelIdsMessage";
import { ReplyChannelRangeMessage } from "./messages/ReplyChannelRangeMessage";
import { ReplyShortChannelIdsEndMessage } from "./messages/ReplyShortChannelIdsEndMessage";
import { ShutdownMessage } from "./messages/ShutdownMessage";
import { MessageType } from "./MessageType";

export function deserialize(buffer: Buffer): IWireMessage {
Expand All @@ -34,6 +35,8 @@ export function deserialize(buffer: Buffer): IWireMessage {
return OpenChannelMessage.deserialize(buffer);
case MessageType.AcceptChannel:
return AcceptChannelMessage.deserialize(buffer);
case MessageType.Shutdown:
return ShutdownMessage.deserialize(buffer);

// gossip messages
case MessageType.NodeAnnouncement:
Expand Down
2 changes: 1 addition & 1 deletion packages/wire/lib/MessageType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export enum MessageType {
FundingCreated = 34,
FundingSigned = 35,
FundingLocked = 36,
CloseChannel = 38,
Shutdown = 38,
ClosingSigned = 39,

// Commitment (128-255)
Expand Down
66 changes: 66 additions & 0 deletions packages/wire/lib/messages/ShutdownMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { BufferReader, BufferWriter } from "@node-lightning/bufio";
import { MessageType } from "../MessageType";
import { ChannelId } from "@node-lightning/core";
import { IWireMessage } from "./IWireMessage";

/**
* ShutdownMessage represents the `shutdown` message defined in BOLT #2 of the Lightning
* Specification. This message can be sent by either node. The scriptPubKey must be a valid P2WPKH,
* P2WSH, P2SH-P2WPKH, P2SH-P2WSH, or any valid witness script if option_shutdown_anysegwit is
* negotiated and it should be same as `shutdown_scriptpubkey` value if it was sent during
* `'open/accept' channel message`. Therefore, if both conditions hold true resulting transaction
* will propagate to miners. If shutdown is sent by either node, corresponding node should send
* commitment_signed to commit any outstanding changes before replying shutdown. Once shutdown is
* sent by both nodes no new HTLCs should be added or accepted by the channel. After successful
* handshake of shutdown message, fee negotiation and signature sending can begin with
* `closing_signed` message.
*/
export class ShutdownMessage implements IWireMessage {
public static type = MessageType.Shutdown;

/**
* Deserializes a shutdown message
* @param buf
*/
public static deserialize(buf: Buffer): ShutdownMessage {
const instance = new ShutdownMessage();
const reader = new BufferReader(buf);

reader.readUInt16BE(); // read type
instance.channelId = new ChannelId(reader.readBytes(32));
const len = reader.readUInt16BE();
instance.scriptPubKey = reader.readBytes(len);

return instance;
}

/**
* The type for message. Shutdown = 38
*/
public readonly type: MessageType = ShutdownMessage.type;

/**
* ChannelId generated from the funding transactions outpoint.
*/
public channelId: ChannelId;

/**
* scriptPubKey is used by the sender to get paid, it must be a valid P2WPKH, P2WSH, P2SH-P2WPKH,
* P2SH-P2WSH, or any valid witness script if option_shutdown_anysegwit is negotiated and it
* should be same as `shutdown_scriptpubkey` value if it was sent during `'open/accept' channel
* message`.
*/
public scriptPubKey: Buffer;

/**
* Serializes the message into a Buffer
*/
public serialize(): Buffer {
const writer = new BufferWriter();
writer.writeUInt16BE(this.type);
writer.writeBytes(this.channelId.toBuffer());
writer.writeUInt16BE(this.scriptPubKey.length);
writer.writeBytes(this.scriptPubKey);
return writer.toBuffer();
}
}