Skip to content
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
130 changes: 130 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp
.cache

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
70 changes: 68 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,69 @@
# request-for-code
# Bitcoin-like Server Project

This repository contains a bunch of interesting requests for people to code. These are things I've always wanted to build but don't get the time to get to them.
## Overview

This project implements a simplified Bitcoin-like server, designed to simulate key aspects of the Bitcoin network. The project consists of a central WebSocket server and multiple miner servers. Each miner maintains its own transaction pool, mines blocks at regular intervals, and interacts with the central server to receive new transactions.

## Architecture

The project consists of the following components:

1. **Central Server** (`centralServer.js`)
- Acts as the central hub for broadcasting transactions to all connected miners.
- Manages WebSocket connections and ensures that all miners receive the latest transactions.
- Facilitates decentralized block mining by keeping miners in sync with the transaction pool.

2. **Miner Server** (`minerServer.js`)
- Each miner operates independently, maintaining its own transaction pool.
- Mines blocks at a fixed interval (e.g., every 30 seconds).
- Interacts with the central server to receive new transactions and broadcast mined blocks.

3. **Blockchain Utilities** (`blockchainUtils.js`)
- Contains utility functions for blockchain operations, including block creation, transaction handling, and validation.
- Ensures consistent and reliable blockchain functionality across all servers.

## Usage

Use the `fund-wallets.txt` file to view the initial accounts created to send transactions.

### API Endpoints

The following APIs are exposed via an Express server:

- **Send Transaction**: Allows sending transactions to a recipient.
- **Endpoint**: `POST /sendTransaction`
- **Request Body**:
- `recipient` (string): The recipient's address (40 characters).
- `amount` (number): The amount to send (must be greater than 0).
- `privateKey` (string): The private key for signing the transaction (64 characters).
- **Response**: `Transaction processed` if successful, with error messages for invalid inputs.

- **Get Balances**: Retrieves the balance of all accounts.
- **Endpoint**: `GET /getBalances`
- **Response**: A JSON object containing all account balances.

- **Get Block Height**: Retrieves the current block height of the blockchain.
- **Endpoint**: `GET /getBlockHeight`
- **Response**: A JSON object with the current block height.

- **Get Balance**: Retrieves the balance of a specific address.
- **Endpoint**: `GET /getBalance/:address`
- **Response**: A JSON object with the balance of the specified address.

### Running the Servers

To run the project, follow these steps:

1. **Start the Central Server**:
```bash
node centralServer.js

2. **Start the Miner Servers**:
```bash
node minerServer.js
```

3. **Access the API Endpoints**:
- Send transactions to the central server using the provided APIs.
- View the balances of all accounts using the `/getBalances` endpoint.
- Monitor the block height using the `/getBlockHeight` endpoint.
61 changes: 61 additions & 0 deletions blockchainUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// blockchainUtils.js
const crypto = require('crypto');
const EC = require('elliptic').ec;
const ec = new EC('secp256k1');

function generateBlockHash(block) {
return crypto.createHash('sha256')
.update(block.previousHash + JSON.stringify(block.transactions) + block.nonce + block.timestamp + block.index)
.digest('hex');
}

function publicKeyToAddress(publicKey) {
const sha256 = crypto.createHash('sha256').update(publicKey, 'hex').digest();
const ripemd160 = crypto.createHash('ripemd160').update(sha256).digest('hex');
return ripemd160;
}

function createTransaction(publicKey, senderAddress, recipient, amount, privateKey) {
const key = ec.keyFromPrivate(privateKey, 'hex');

const transaction = {
publicKey,
senderAddress,
recipient,
amount,
timestamp: Date.now(),
};

const transactionData = publicKey + senderAddress + recipient + amount + transaction.timestamp;
const transactionHash = crypto.createHash('sha256').update(transactionData).digest('hex');
const signature = key.sign(crypto.createHash('sha256').update(transactionData).digest()).toDER('hex');

transaction.hash = transactionHash;
transaction.signature = signature;
return transaction;
}

function validateTransaction(transaction, balances) {
const { publicKey, senderAddress , recipient, amount, timestamp, signature } = transaction;
const key = ec.keyFromPublic(publicKey, 'hex');
const isValid = key.verify(crypto.createHash('sha256').update(publicKey + senderAddress + recipient + amount + timestamp).digest(), signature);


if (balances[senderAddress] === undefined || balances[senderAddress] < amount) {
return false;
}
return isValid;
}

function validateBlock(block, difficulty) {
const hash = generateBlockHash(block);
return hash.substring(0, difficulty) === '0'.repeat(difficulty);
}

module.exports = {
generateBlockHash,
publicKeyToAddress,
createTransaction,
validateTransaction,
validateBlock,
};
Loading