Skip to content

Praktikum Blockchain: Implementasi Desentralisasi (Edisi Linux Final)

Klik link berikut untuk melihat sumber asli Referensi Praktikum Blockchain

Panduan ini adalah versi lengkap yang mencakup pembuatan core blockchain hingga pembangunan jaringan terdesentralisasi (P2P) yang saling tersinkronisasi.

1. Persiapan Lingkungan (Linux)

1.1 Instalasi Dependensi

Pastikan Anda berada di direktori proyek dan jalankan perintah berikut untuk menginstal modul yang diperlukan:

npm install express body-parser nodemon uuid request request-promise sha256 --save

1.2 Konfigurasi Multi-Node (package.json)

Untuk mensimulasikan banyak komputer (node) dalam satu mesin Linux, tambahkan konfigurasi berikut pada bagian scripts di file package.json:

"scripts": {
  "start": "nodemon --watch src -e js src/api.js 3000 http://localhost:3000",
  "node1": "nodemon --watch src -e js src/api.js 3001 http://localhost:3001",
  "node2": "nodemon --watch src -e js src/api.js 3002 http://localhost:3002",
  "node3": "nodemon --watch src -e js src/api.js 3003 http://localhost:3003",
  "node4": "nodemon --watch src -e js src/api.js 3004 http://localhost:3004",
  "node5": "nodemon --watch src -e js src/api.js 3005 http://localhost:3005"
}

2. Core Blockchain Terdesentralisasi (src/blockchain.js)

File ini menangani logika utama rantai blok, pembuatan transaksi dengan ID unik, dan mekanisme Proof of Work.

const sha256 = require('sha256');
const nodeUrl = process.argv[3];
const { v4: uuidv4 } = require('uuid');

class Block {
    constructor(index, timestamp, nonce, prevBlockHash, hash, transactions) {
        this.index = index;
        this.timestamp = timestamp;
        this.transactions = transactions;
        this.nonce = nonce;
        this.hash = hash;
        this.prevBlockHash = prevBlockHash;
    }
}

class Blockchain {
    constructor() {
        this.chain = [];
        this.pendingTransactions = [];
        this.nodeUrl = nodeUrl;
        this.networkNodes = [];
        // Genesis Block
        this.createNewBlock(100, '0', 'Genesis block');
    }

    createNewBlock(nonce, prevBlockHash, hash) {
        const newBlock = new Block(
            this.chain.length + 1,
            Date.now(),
            nonce,
            prevBlockHash,
            hash,
            this.pendingTransactions
        );
        this.pendingTransactions = [];
        this.chain.push(newBlock);
        return newBlock;
    }

    getLatestBlock() {
        return this.chain[this.chain.length - 1];
    }

    makeNewTransaction(amount, sender, recipient) {
        return {
            amount: amount,
            sender: sender,
            recipient: recipient,
            id: uuidv4().split('-').join('')
        };
    }

    addTransactionToPendingTransactions(transaction) {
        this.pendingTransactions.push(transaction);
        return this.getLatestBlock().index + 1;
    }

    hashBlock(prevBlockHash, currentBlock, nonce) {
        const data = prevBlockHash + JSON.stringify(currentBlock) + nonce;
        return sha256(data);
    }

    proofOfWork(prevBlockHash, currentBlockData) {
        let nonce = 0;
        let hash = this.hashBlock(prevBlockHash, currentBlockData, nonce);
        while (hash.substring(0, 2) !== '00') {
            nonce++;
            hash = this.hashBlock(prevBlockHash, currentBlockData, nonce);
        }
        return nonce;
    }
}

module.exports = Blockchain;

3. API & Sinkronisasi Jaringan (src/api.js)

File ini memungkinkan blockchain berkomunikasi antar node menggunakan Express.js.

const express = require('express');
const app = express();
const { v4: uuidv4 } = require('uuid');
const nodeAddr = uuidv4().split('-').join('');
const reqPromise = require('request-promise');
const Blockchain = require('./blockchain');
const bitcoin = new Blockchain();

app.use(express.json());

// Endpoint untuk melihat data blockchain
app.get('/blockchain', (req, res) => res.send(bitcoin));

// Endpoint broadcast transaksi ke seluruh jaringan
app.post('/transaction/broadcast', (req, res) => {
    const transaction = bitcoin.makeNewTransaction(req.body.amount, req.body.sender, req.body.recipient);
    bitcoin.addTransactionToPendingTransactions(transaction);

    const requests = bitcoin.networkNodes.map(node => {
        return reqPromise({ uri: node + '/transaction', method: 'POST', body: transaction, json: true });
    });

    Promise.all(requests).then(() => res.json({ message: 'Transaction broadcasted!' }));
});

// Endpoint untuk menerima transaksi dari node lain
app.post('/transaction', (req, res) => {
    const blockIndex = bitcoin.addTransactionToPendingTransactions(req.body);
    res.json({ message: `Transaction added to pending at index ${blockIndex}` });
});

// Endpoint Mining & Broadcast Blok
app.get('/mine', (req, res) => {
    const lastBlock = bitcoin.getLatestBlock();
    const prevBlockHash = lastBlock.hash;
    const currentBlockData = { transactions: bitcoin.pendingTransactions, index: lastBlock.index + 1 };
    const nonce = bitcoin.proofOfWork(prevBlockHash, currentBlockData);
    const blockHash = bitcoin.hashBlock(prevBlockHash, currentBlockData, nonce);
    const newBlock = bitcoin.createNewBlock(nonce, prevBlockHash, blockHash);

    const requests = bitcoin.networkNodes.map(node => {
        return reqPromise({ uri: node + '/add-block', method: 'POST', body: { newBlock: newBlock }, json: true });
    });

    Promise.all(requests)
        .then(() => reqPromise({
            uri: bitcoin.nodeUrl + '/transaction/broadcast',
            method: 'POST',
            body: { amount: 1, sender: "00", recipient: nodeAddr },
            json: true
        }))
        .then(() => res.json({ message: "Mining & broadcasting successful!", newBlock: newBlock }));
});

// Endpoint untuk menerima blok dari node lain
app.post('/add-block', (req, res) => {
    const { newBlock } = req.body;
    const lastBlock = bitcoin.getLatestBlock();
    if (lastBlock.hash === newBlock.prevBlockHash && lastBlock.index + 1 === newBlock.index) {
        bitcoin.chain.push(newBlock);
        bitcoin.pendingTransactions = [];
        res.json({ message: 'Block accepted' });
    } else {
        res.json({ message: 'Block rejected' });
    }
});

// Endpoint Registrasi Jaringan (P2P)
app.post('/register-and-broadcast-node', (req, res) => {
    const { nodeUrl } = req.body;
    if (!bitcoin.networkNodes.includes(nodeUrl)) bitcoin.networkNodes.push(nodeUrl);

    const registerNodes = bitcoin.networkNodes.map(node => {
        return reqPromise({ uri: node + '/register-node', method: 'POST', body: { nodeUrl: nodeUrl }, json: true });
    });

    Promise.all(registerNodes)
        .then(() => reqPromise({
            uri: nodeUrl + '/register-bulk-nodes',
            method: 'POST',
            body: { networkNodes: [...bitcoin.networkNodes, bitcoin.nodeUrl] },
            json: true
        }))
        .then(() => res.json({ message: 'Node registered with network successfully!' }));
});

app.post('/register-node', (req, res) => {
    const { nodeUrl } = req.body;
    if (!bitcoin.networkNodes.includes(nodeUrl) && bitcoin.nodeUrl !== nodeUrl) {
        bitcoin.networkNodes.push(nodeUrl);
        res.json({ message: 'Node registered locally.' });
    } else res.json({ message: 'Registration failed.' });
});

app.post('/register-bulk-nodes', (req, res) => {
    req.body.networkNodes.forEach(node => {
        if (!bitcoin.networkNodes.includes(node) && bitcoin.nodeUrl !== node) bitcoin.networkNodes.push(node);
    });
    res.json({ message: 'Bulk registration successful.' });
});

const port = process.argv[2];
app.listen(port, () => console.log(`Node running on port ${port}`));

4. Simulasi Jaringan & Pengujian Postman

4.1 Menjalankan Multi-Node

Buka dua terminal Linux berbeda dan jalankan:

  • Terminal 1: npm run node1 (Port 3001)
  • Terminal 2: npm run node2 (Port 3002)

4.2 Langkah-Langkah Tes Jaringan (Postman)

  1. Hubungkan Node 2 ke Node 1:
    • URL:
      http://localhost:3001/register-and-broadcast-node
      
    • Method: POST (Body JSON):
      {
          "nodeUrl": "http://localhost:3002"
      }
      
  2. Kirim Transaksi ke Node 1:

    • URL:

      http://localhost:3001/transaction/broadcast
      

    • Method: POST (Body JSON):

      {
          "amount": 100, "sender": "ALICE", "recipient": "BOB"
      }
      

  3. Verifikasi di Node 2:

    • URL(GET):
      http://localhost:3002/blockchain
      
      (Cek apakah transaksi ALICE muncul di pendingTransactions Node 2).
  4. Mining di Node 2:
    • URL(GET):
      http://localhost:3002/mine
      
  5. Cek Sinkronisasi Akhir:
    • Panggil GET /blockchain di Node 1 dan Node 2. Keduanya harus memiliki blok baru yang sama.

4.3 Tips Linux

Gunakan perintah lsof -i :3001 untuk mengecek jika port masih terkunci, dan kill -9 <PID> untuk membebaskannya.