Building a Simple Gossip Protocol with Node.js and TypeScript
Table of contents
- Introduction
- Theoretical Background
- Building a Simple Gossip Protocol in Node.js and TypeScript
- Step 1: Set Up the Project
- Step 2: Create the GossipNode Class
- Step 3: Set Up Nodes and Simulate a Transaction Broadcast
- Step 4: Run the Gossip Nodes
- Key Concepts Recap
- Conclusion
- Further Reading and Research Resources
Introduction
Gossip protocols are a communication model used in distributed systems to disseminate information efficiently across a network without centralized coordination. These protocols are commonly found in large-scale systems like blockchain networks, databases, and P2P file-sharing systems due to their scalability, resilience, and reliability.
In this guide, you'll build a simple gossip protocol in TypeScript using Node.js. You’ll also extend the protocol to simulate the broadcasting of blockchain transactions, allowing each node to receive and propagate transactions to peers. By the end, you'll understand both the basic theory of gossip protocols and the implementation steps to build a functional gossip network.
Theoretical Background
What is a Gossip Protocol?
A gossip protocol is a communication model where nodes (or peers) in a network spread information by randomly sharing it with a few neighbors. This process is repeated until all nodes in the network receive the information. Like the concept of spreading a rumor, gossip protocols are resilient, eventually consistent, and adapt well to network changes.
Key Components of a Gossip Protocol
Messages: The data exchanged between nodes. Messages may include a unique identifier to avoid duplication and a Time-To-Live (TTL) to limit propagation.
Nodes: Each node is an individual process or instance in the network that can send, receive, and relay messages.
Peer Selection: Nodes randomly select a subset of their peers to forward messages, creating a dynamic, non-centralized message flow.
Propagation Rules: To prevent duplicate messages, nodes track received messages by their unique IDs and only forward messages that are new.
Time-To-Live (TTL): Each message has a TTL to limit the number of hops it can take, helping control network congestion and prevent indefinite propagation.
Resilience and Fault Tolerance: Gossip protocols are robust against node failures because other nodes continue to relay messages independently.
Building a Simple Gossip Protocol in Node.js and TypeScript
Overview of the Project
This guide will help you build a decentralized network where nodes can send and receive messages. Each node will have a unique port, enabling separate terminal instances to simulate the behavior of individual nodes. We'll also implement a simulated "transaction" to represent a blockchain transaction being broadcast through the gossip network.
Step 1: Set Up the Project
Create a new project directory:
mkdir gossip-protocol cd gossip-protocol
Initialize a pnpm project and install dependencies:
pnpm init pnpm add -D typescript tsx @types/node
Configure TypeScript by creating a
tsconfig.json
file:{ "compilerOptions": { "target": "es6", "module": "commonjs", "strict": true, "esModuleInterop": true, "outDir": "./dist" }, "include": ["src/**/*"] }
Set up the project structure by creating a
src
folder with two files:mkdir src touch src/gossipNode.ts src/index.ts
Step 2: Create the GossipNode Class
The GossipNode
class handles each node's behavior, including receiving, processing, and gossiping messages to peers.
1. Define the Node’s Message Structure and Initial Imports
In src/gossipNode.ts
:
import { createSocket, type Socket } from 'node:dgram';
import { randomUUID } from 'node:crypto';
interface Message {
messageId: string;
content: string;
ttl: number;
}
2. Define the GossipNode Class Structure
The GossipNode
class is where the core gossip protocol logic resides. Each node will:
Receive incoming messages
Track messages to avoid duplication
Gossip messages to a random subset of peers
class GossipNode {
private socket: Socket;
private receivedMessages: Set<string> = new Set();
constructor(private port: number, private peers: { ip: string; port: number }[]) {
this.socket = createSocket('udp4');
}
start() {
this.socket.on('message', (msg, rinfo) => {
const message: Message = JSON.parse(msg.toString());
this.handleMessage(message);
});
this.socket.bind(this.port, () => {
console.log(`Node listening on port ${this.port}`);
});
}
private handleMessage(message: Message) {
if (!this.receivedMessages.has(message.messageId) && message.ttl > 0) {
console.log(`Node ${this.port} received message: ${message.content}`);
this.receivedMessages.add(message.messageId);
this.gossip({ ...message, ttl: message.ttl - 1 });
}
}
sendMessage(content: string) {
const messageId = randomUUID();
const message: Message = { messageId, content, ttl: 3 };
this.receivedMessages.add(messageId);
this.gossip(message);
}
private gossip(message: Message) {
const peersToSend = this.getRandomPeers(2);
peersToSend.forEach((peer) => {
this.socket.send(
JSON.stringify(message),
peer.port,
peer.ip,
(error) => {
if (error) {
console.error(`Error sending to ${peer.ip}:${peer.port}`);
}
}
);
});
}
private getRandomPeers(count: number) {
return this.peers
.sort(() => 0.5 - Math.random())
.slice(0, Math.min(count, this.peers.length));
}
}
export default GossipNode;
Step 3: Set Up Nodes and Simulate a Transaction Broadcast
To simulate a network where nodes gossip a "transaction" message, we’ll set up nodes in src/index.ts
.
import GossipNode from "./gossipNode";
// Define nodes and peers for a small network
const nodes = [
{ ip: '127.0.0.1', port: 5001 },
{ ip: '127.0.0.1', port: 5002 },
{ ip: '127.0.0.1', port: 5003 },
{ ip: '127.0.0.1', port: 5004 },
];
// Initialize a GossipNode instance
const nodeId = parseInt(process.argv[2] || '0');
const { ip, port } = nodes[nodeId];
const peers = nodes.filter((node) => node.port !== port);
const gossipNode = new GossipNode(port, peers);
gossipNode.start();
// Simulate a "transaction" broadcast from the first node
if (nodeId === 0) {
setTimeout(() => {
gossipNode.sendMessage("🔗 New transaction: 0x123abc");
}, 2000); // Wait a bit for other nodes to start listening
}
Step 4: Run the Gossip Nodes
Open multiple terminals, one for each node, and start each node by specifying its ID as a command-line argument:
pnpm tsx src/index.ts 1 pnpm tsx src/index.ts 2 pnpm tsx src/index.ts 3 # Start Node 0 last, so it can send the transaction message when others are ready pnpm tsx src/index.ts 0
The first node (Node 0) will initiate a simulated transaction message. Each node will propagate the message to a random subset of its peers until all nodes have received the message.
Key Concepts Recap
Unique Transaction ID: Each "transaction" has a unique message ID to prevent duplicates.
Peer Gossiping: Nodes randomly select peers to send messages, allowing for efficient dissemination.
Transaction TTL: A
ttl
property limits message propagation to avoid network congestion.
Conclusion
You now have a functioning gossip protocol that simulates the spread of a blockchain transaction. This setup showcases the decentralized, resilient nature of gossip networks, which are essential to maintaining data consistency in distributed systems like blockchains.
Further Reading and Research Resources
To deepen understanding and explore real-world implementations of enhanced gossip protocols in blockchain systems, consider the following materials:
Articles:
Books and Tutorials:
- Distributed Systems: Principles and Paradigms by Andrew S. Tanenbaum and Maarten Van Steen – Covers core distributed system principles, including gossip protocols and fault tolerance.
Blockchain-Specific Implementations:
- LibP2P (used by IPFS and Ethereum 2.0) – A modular network stack that implements gossiping with peer-to-peer components, allowing exploration of real-world blockchain gossiping mechanisms.
Subscribe to my newsletter
Read articles from Avoaja Joshua Akachi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by