Lightning Network Noise Protocol Socket (BOLT #8)
Implements a Noise Protocol TCP Socket and Server in Node.js as defined in Lightning Network BOLT #8 for Encrypted and Authenticated Transport.
NoiseSocket
enables confidentiality between two nodes by encrypting all traffic between them. All traffic is also authenticated against the node's known long-term identifier (Bitcoin public key as curve secp256k1).
NoiseSocket
performs a cryptographic handshake upon connection and performs frequent key rotation. Once the handshake is complete, the ready
event signals that messages can be sent or received.
NoiseSocket
is a decorator for net.Socket
. It maintains the same interface used by net.Socket
and implements a Duplex
stream. This enables reading from the stream in paused (readable
event) or flowing mode (piped or data
event).
Learn more about the Noise Protocol and the Lighting Network version:
Requirements
Usage
Creating a NoiseSocket Client
This example shows how to create a NoiseSocket
that connects to a NoiseServer
.
To create a NoiseSocket
, you must provide:
- local private key
ls
which is a Buffer
with 32-bytes - remote node's compressed public key
rp
which is a Buffer
with 33-bytes.
These two values are required to create the encrypted and authenticated communication channel.
const noise = require("@node-lightning/noise");
const ls = Buffer.from("1111111111111111111111111111111111111111111111111111111111111111", "hex");
const rpk = Buffer.from(
"028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7",
"hex",
);
const socket = noise.connect({ ls, rpk, host: "127.0.0.1", port: 9735 });
socket.on("connect", () => {
console.log("connected to server");
});
socket.on("ready", () => {
console.log("handshake complete, ready for sending/receiving");
socket.write(Buffer.from("hello from client"));
});
socket.on("data", buf => {
console.log("received: " + buf.toString());
});
Creating a NoiseSocket Server:
const noise = require('@node-lightning/noise');
const ls = Buffer.from('2121212121212121212121212121212121212121212121212121212121212121', 'hex');
const server = noise.createServer({ ls }, onSocket);
server.listen({ host: '127.0.0.1', port: 9735 });
function onSocket(socket) {
console.log('remote socket connected');
socket.on('ready', () => {
console.log('handshake complete, ready for sending/receiving');
socket.write(Buffer.from('hello from server'));
});
socket.on('data', buf => {
console.log('received: ' + buf.toString());
});
});
Reading Modes
Because Noisesocket is a Duplex
stream and fully implements Readable
functionality, it allows reading from the stream via both reading modes: flowing and paused.
Flowing mode is the recommended technique for consuming data and can be achieved by using the data
event or piping the NoiseSocket to another stream.
The data
event method reads data as soon as it comes in:
socket.on("data", buf => {
});
You can pipe responses to other streams:
let fs = require("fs");
let wr = fs.createWriteStream("capture.bin");
socket.pipe(wr);
Lastly, you can use paused mode to manually read from the stream. readable
events will be emitted when new data is available. This mechanism allows you to directly control how reads occur and is useful
for implementing wire protocols ontop of the NoiseSocket
.
socket.on("readable", () => {
let buf = socket.read();
if (buf) {
}
});
More information about reading modes is available in the Node.js streams documentation.
Writing
Writing to the socket can be accomplished by calling write
and passing in a Buffer.
let d = Buffer.from("some data");
socket.write(d);
The write
method will return a boolean value indicating if you can continue to write to the socket. If write
returns false, you must wait until the drain
event is emitted before continuing to write data. If you do not respond to to the boolean flag, data will be buffered into memory.