Building a Blockchain Node with JavaScript
/ 6 min read
Table of Contents
Building a Blockchain Node with JavaScript
Blockchain technology continues to evolve beyond cryptocurrency applications into enterprise solutions. This guide demonstrates how to build a functioning blockchain node using Node.js, providing you with fundamental knowledge of distributed ledger architecture.
Blockchain Fundamentals
Before diving into code, let’s understand key blockchain components:
- Block structure: Contains transactions, timestamp, and cryptographic hash
- Consensus mechanism: How nodes agree on the valid state
- Peer-to-peer networking: How nodes communicate
- Cryptographic validation: Ensuring data integrity
Setting Up the Project
First, create a new Node.js project and install dependencies:
mkdir blockchain-nodecd blockchain-nodenpm init -ynpm install crypto-js express body-parser ws uuid
Creating the Block Structure
Let’s define our block structure with TypeScript:
import SHA256 from 'crypto-js/sha256';
interface Transaction { sender: string; recipient: string; amount: number; timestamp: number;}
class Block { index: number; timestamp: number; transactions: Transaction[]; previousHash: string; hash: string; nonce: number;
constructor( index: number, timestamp: number, transactions: Transaction[], previousHash: string = '' ) { this.index = index; this.timestamp = timestamp; this.transactions = transactions; this.previousHash = previousHash; this.nonce = 0; this.hash = this.calculateHash(); }
calculateHash(): string { return SHA256( this.index + this.previousHash + this.timestamp + JSON.stringify(this.transactions) + this.nonce ).toString(); }
mineBlock(difficulty: number): void { const target = Array(difficulty + 1).join('0');
while (this.hash.substring(0, difficulty) !== target) { this.nonce++; this.hash = this.calculateHash(); }
console.log(`Block mined: ${this.hash}`); }}
Implementing the Blockchain
Now, let’s create the blockchain class to manage blocks:
class Blockchain { chain: Block[]; difficulty: number; pendingTransactions: Transaction[]; miningReward: number;
constructor() { this.chain = [this.createGenesisBlock()]; this.difficulty = 4; this.pendingTransactions = []; this.miningReward = 100; }
createGenesisBlock(): Block { return new Block(0, Date.now(), [], '0'); }
getLatestBlock(): Block { return this.chain[this.chain.length - 1]; }
minePendingTransactions(miningRewardAddress: string): void { const rewardTx: Transaction = { sender: 'SYSTEM', recipient: miningRewardAddress, amount: this.miningReward, timestamp: Date.now() };
this.pendingTransactions.push(rewardTx);
const block = new Block( this.chain.length, Date.now(), this.pendingTransactions, this.getLatestBlock().hash );
block.mineBlock(this.difficulty); console.log('Block successfully mined!');
this.chain.push(block); this.pendingTransactions = []; }
addTransaction(transaction: Transaction): void { // Validate transaction if (!transaction.sender || !transaction.recipient || transaction.amount <= 0) { throw new Error('Invalid transaction'); }
// Add to pending transactions this.pendingTransactions.push(transaction); }
getBalanceOfAddress(address: string): number { let balance = 0;
for (const block of this.chain) { for (const trans of block.transactions) { if (trans.sender === address) { balance -= trans.amount; }
if (trans.recipient === address) { balance += trans.amount; } } }
return balance; }
isChainValid(): boolean { for (let i = 1; i < this.chain.length; i++) { const currentBlock = this.chain[i]; const previousBlock = this.chain[i - 1];
if (currentBlock.hash !== currentBlock.calculateHash()) { return false; }
if (currentBlock.previousHash !== previousBlock.hash) { return false; } }
return true; }}
Building the P2P Network
Next, let’s create a peer-to-peer network to synchronize blockchain data across nodes:
import WebSocket from 'ws';import { v4 as uuidv4 } from 'uuid';
interface PeerMessage { type: string; data: any;}
class P2PServer { private blockchain: Blockchain; private sockets: WebSocket[]; private nodeId: string;
constructor(blockchain: Blockchain) { this.blockchain = blockchain; this.sockets = []; this.nodeId = uuidv4(); }
listen(port: number): void { const server = new WebSocket.Server({ port });
server.on('connection', (socket) => this.connectSocket(socket));
console.log(`Listening for peer connections on port: ${port}`); }
connectToPeers(newPeers: string[]): void { newPeers.forEach((peer) => { const socket = new WebSocket(peer);
socket.on('open', () => this.connectSocket(socket)); socket.on('error', () => console.log('Connection failed')); }); }
connectSocket(socket: WebSocket): void { this.sockets.push(socket); console.log('Socket connected');
this.messageHandler(socket); this.sendChain(socket); }
messageHandler(socket: WebSocket): void { socket.on('message', (data) => { const message: PeerMessage = JSON.parse(data.toString());
switch (message.type) { case 'CHAIN': this.handleChainMessage(message.data); break; case 'TRANSACTION': this.handleTransactionMessage(message.data); break; } }); }
sendChain(socket: WebSocket): void { socket.send(JSON.stringify({ type: 'CHAIN', data: this.blockchain.chain })); }
syncChains(): void { this.sockets.forEach(socket => this.sendChain(socket)); }
broadcastTransaction(transaction: Transaction): void { this.sockets.forEach(socket => { socket.send(JSON.stringify({ type: 'TRANSACTION', data: transaction })); }); }
handleChainMessage(chain: Block[]): void { const receivedChain = chain.map(block => { const reconstructedBlock = new Block( block.index, block.timestamp, block.transactions, block.previousHash ); reconstructedBlock.hash = block.hash; reconstructedBlock.nonce = block.nonce; return reconstructedBlock; });
if (receivedChain.length > this.blockchain.chain.length) { console.log('Received chain is longer than current chain. Replacing chain.'); this.blockchain.chain = receivedChain; } }
handleTransactionMessage(transaction: Transaction): void { this.blockchain.addTransaction(transaction); }}
Creating a REST API
Now, let’s create an API to interact with our blockchain:
import express from 'express';import bodyParser from 'body-parser';
const app = express();app.use(bodyParser.json());
const blockchain = new Blockchain();const p2pServer = new P2PServer(blockchain);
// Get the entire blockchainapp.get('/blocks', (req, res) => { res.json(blockchain.chain);});
// Mine a new blockapp.post('/mine', (req, res) => { const { minerAddress } = req.body;
if (!minerAddress) { return res.status(400).json({ error: 'Miner address is required' }); }
blockchain.minePendingTransactions(minerAddress); p2pServer.syncChains();
res.redirect('/blocks');});
// Create a new transactionapp.post('/transaction', (req, res) => { const { sender, recipient, amount } = req.body;
if (!sender || !recipient || !amount) { return res.status(400).json({ error: 'Invalid transaction data' }); }
const transaction: Transaction = { sender, recipient, amount, timestamp: Date.now() };
blockchain.addTransaction(transaction); p2pServer.broadcastTransaction(transaction);
res.json({ success: true, transaction });});
// Get account balanceapp.get('/balance/:address', (req, res) => { const balance = blockchain.getBalanceOfAddress(req.params.address); res.json({ address: req.params.address, balance });});
// Connect to peersapp.post('/peers', (req, res) => { const { peers } = req.body;
if (!peers || !Array.isArray(peers)) { return res.status(400).json({ error: 'Invalid peers data' }); }
p2pServer.connectToPeers(peers); res.json({ success: true });});
const HTTP_PORT = process.env.HTTP_PORT || 3001;app.listen(HTTP_PORT, () => { console.log(`HTTP server listening on port ${HTTP_PORT}`);});
const P2P_PORT = process.env.P2P_PORT || 5001;p2pServer.listen(P2P_PORT);
Running Multiple Nodes
To test a distributed network, you can run multiple nodes on your local machine:
# Terminal 1: Start the first nodeHTTP_PORT=3001 P2P_PORT=5001 npm start
# Terminal 2: Start the second nodeHTTP_PORT=3002 P2P_PORT=5002 PEERS=ws://localhost:5001 npm start
# Terminal 3: Start the third nodeHTTP_PORT=3003 P2P_PORT=5003 PEERS=ws://localhost:5001,ws://localhost:5002 npm start
Implementing Proof of Work
Our blockchain already includes a simple proof-of-work mechanism through the mineBlock
method. This ensures that creating new blocks requires computational effort, making it difficult for attackers to manipulate the chain.
By adjusting the difficulty
parameter, you can control how much work is needed to mine a block.
Security Considerations
This implementation is for educational purposes. For production systems, consider:
- Wallet security: Implement proper public/private key infrastructure
- Data validation: Add comprehensive transaction validation
- Network security: Add peer authentication
- Scalability solutions: Implement smart chain pruning and optimization
- Consensus improvements: Explore PoS or other efficient consensus mechanisms
Conclusion
You now have a functional blockchain node in JavaScript that demonstrates the core concepts of blockchain technology. You can continue to enhance this implementation by adding:
- Digital signatures for transaction authentication
- More advanced consensus algorithms
- Smart contract functionality
- Chain pruning mechanisms
- UTXO or account-based transaction models
Blockchain technology continues to evolve rapidly, with new approaches to scaling, privacy, and interoperability emerging in 2025. By understanding the fundamentals presented here, you’ll be better positioned to work with more complex blockchain systems in production environments.