Skip to content

Create blockchain like server #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
36 changes: 36 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
node_modules
.pnp
.pnp.js
.yarn/install-state.gz

# testing
coverage

# next.js
.next/
out/

# production
build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
52 changes: 52 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Bitcoin-like Server

This code provides the Backend for a Bitcoin-like server. where it has central websocket server that all miners connect to to exchange messages. Code that miners can run to be able to create blocks, do proof of work, broadcast the block via the central server. Code that verifies the signature, balances and creates / adds a block. It It able to catch up to the blockchain when the server starts.
## Installation

To set up the development environment, follow these steps:

1. Navigate to the frontend directory:
```bash
cd backend
```

2. Install all required dependencies:
```bash
npm install
```

3. Start the development server:
```bash
nodemon app.ts
```
4. Start the central server:
```bash
cd central-server
nodemon index.ts
```

5. Start the miner server:
```bash
cd miner-server
nodemon index.ts
```

6. Setup your frontend directory and start it

7. Open your browser and navigate to [http://localhost:3000](http://localhost:3000) to view the application.

## Testing the Application

Before testing the frontend application, ensure that the backend server is running without any errors.

### Steps to Test

1. Open [http://localhost:3000](http://localhost:3000) in your browser.

2. Open a new tab and enter [http://localhost:3000/all-blocks](http://localhost:3000/all-blocks). You should see a display showing 0 blocks created initially.

3. On the homepage (`/`), create a new wallet and submit a transaction.

4. Return to the "All Blocks" page. You should see that the newly created block has been added successfully.

##
52 changes: 52 additions & 0 deletions backend/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import express from "express";
import * as bip39 from "bip39";
import * as bitcoin from "bitcoinjs-lib";
import BIP32Factory from "bip32";
import * as ecc from "tiny-secp256k1";
import cors from "cors";

// Initialize bip32 with the ECC library
const bip32 = BIP32Factory(ecc);

const network = bitcoin.networks.testnet;
const path = "m/44'/1'/0'/0";

const app = express();
const port = 5000;
app.use(
cors({
origin: "http://localhost:3000",
methods: ["GET", "POST"],
allowedHeaders: ["Origin", "X-Requested-With", "Content-Type"],
credentials: true,
})
);

function generateWallet() {
let mnemonic = bip39.generateMnemonic();
const seed = bip39.mnemonicToSeedSync(mnemonic);
const root = bip32.fromSeed(seed, network);

let account = root.derivePath(path);
let node = account.derive(0).derive(0);

let btcAddress = bitcoin.payments.p2pkh({
pubkey: node.publicKey,
network,
}).address;

return {
address: btcAddress,
key: node.toWIF(),
mnemonic: mnemonic,
};
}

app.get("/create-wallet", (req, res) => {
const wallet = generateWallet();
res.json(wallet);
});

app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
75 changes: 75 additions & 0 deletions backend/central-server/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import express from "express";
import http from "http";
import { Server } from "socket.io";
import crypto from "crypto";
import cors from "cors";

const app = express();
app.use(cors());
const server = http.createServer(app);
const io = new Server(server, {
cors: {
origin: ["http://localhost:3000"],
methods: ["GET", "POST"],
},
});

const blockchain: any[] = [];

// Function to calculate hash for a block
function calculateHash(
index: number,
prevHash: string,
timestamp: number,
data: string
): string {
return crypto
.createHash("sha256")
.update(index + prevHash + timestamp + data)
.digest("hex");
}

// Validate the new block
function isValidNewBlock(newBlock: any, previousBlock: any): boolean {
if (previousBlock.index + 1 !== newBlock.index) return false;
if (previousBlock.hash !== newBlock.prevHash) return false;
if (
calculateHash(
newBlock.index,
newBlock.prevHash,
newBlock.timestamp,
newBlock.data + newBlock.nonce
) !== newBlock.hash
)
return false;

return true;
}

io.on("connection", (socket) => {
console.log("A miner connected");

// Broadcast blockchain to new miners
socket.emit("blockchain", blockchain);

// Listen for new blocks from miners
socket.on("newBlock", (block) => {
const lastBlock =
blockchain.length > 0 ? blockchain[blockchain.length - 1] : null;

// If there is no previous block, accept the first block as the genesis block
if (!lastBlock || isValidNewBlock(block, lastBlock)) {
blockchain.push(block);
io.emit("blockAdded", block);
io.emit("blockchain", blockchain);
console.log("New block added to the blockchain");
console.log(blockchain);
} else {
console.log("Invalid block rejected");
}
});
});

server.listen(3001, () => {
console.log("Central Server listening on port 3001");
});
127 changes: 127 additions & 0 deletions backend/miner-server/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import express from "express";
import http from "http";
import { Server } from "socket.io";
import { io as ClientIO } from "socket.io-client";
import crypto from "crypto";
import cors from "cors";

const app = express();
app.use(cors());
const server = http.createServer(app);
const io = new Server(server, {
cors: {
origin: "http://localhost:3000", // Ensure this is correct
methods: ["GET", "POST"],
},
});
const centralServer = ClientIO("http://localhost:3001"); // Connect to Central Server

let localBlockchain: any[] = [];

// Function to calculate hash for a block
function calculateHash(
index: number,
prevHash: string,
timestamp: number,
data: string
): string {
return crypto
.createHash("sha256")
.update(index + prevHash + timestamp + data)
.digest("hex");
}

// Proof of Work
function proofOfWork(
index: number,
prevHash: string,
timestamp: number,
data: string,
difficulty: number
): { hash: string; nonce: number } {
let nonce = 0;
let hash: string;

do {
nonce++;
hash = calculateHash(index, prevHash, timestamp, data + nonce);
} while (hash.substring(0, difficulty) !== Array(difficulty + 1).join("0"));

return { hash, nonce };
}

// Validate the new block
function isValidNewBlock(newBlock: any, previousBlock: any): boolean {
if (previousBlock.index + 1 !== newBlock.index) return false;
if (previousBlock.hash !== newBlock.prevHash) return false;
if (
calculateHash(
newBlock.index,
newBlock.prevHash,
newBlock.timestamp,
newBlock.data + newBlock.nonce
) !== newBlock.hash
)
return false;

return true;
}

// Handle blockchain updates from central server
centralServer.on("blockchain", (blockchain) => {
localBlockchain = blockchain;
console.log("Blockchain received from central server");
});

// Handle new block from miners
io.on("connection", (socket) => {
let user = socket.id;
console.log("Miner connected to miner server", socket.id);
socket.emit("user", user)

// Send current blockchain to the newly connected miner
socket.emit("blockchain", localBlockchain);

// Listen for new block mining requests
socket.on("mineBlock", (data) => {
const lastBlock =
localBlockchain.length > 0
? localBlockchain[localBlockchain.length - 1]
: null;
const index = localBlockchain.length;
const timestamp = Date.now();
const difficulty = 4; // Define the difficulty level

const { hash, nonce } = proofOfWork(
index,
lastBlock?.hash || "0",
timestamp,
data,
difficulty
);

const newBlock = {
index,
timestamp,
data,
prevHash: lastBlock?.hash || "0",
hash,
nonce,
};

// If there is no previous block, skip the validation for the first block
if (!lastBlock || isValidNewBlock(newBlock, lastBlock)) {
localBlockchain.push(newBlock);
centralServer.emit("newBlock", newBlock);
io.emit("blockAdded", newBlock); // Emit the new block to all connected clients
io.emit("blockchain", localBlockchain); // Update blockchain data for all clients
console.log("New block mined and added to the blockchain", user);
} else {
console.log("Invalid block was not added to the chain");
}
});
});

server.listen(3002, () => {
console.log("Miner Server listening on port 3002");
});
Loading