Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
adds blockchain tests & README
  • Loading branch information
hanibhat committed May 31, 2026
commit 12b9843720494c61d7be76b13f2b6ae517fd9f95
209 changes: 209 additions & 0 deletions src/data-structures/blockchain/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
# Blockchain

## What is Blockchain?

A blockchain is a distributed ledger technology that maintains a chain of blocks, where each block contains cryptographically secured data. Each block is linked to the previous block through a hash reference, creating an immutable chain. Any attempt to modify a past block would change its hash, breaking the chain and making the tampering detectable.

Key characteristics:

- **Immutability**: Once data is recorded, it cannot be altered without detection.
- **Transparency**: All transactions are visible to participants.
- **Decentralization**: No single point of control.
- **Security**: Uses cryptographic hashing to secure data.

![Blockchain](./images/blockchain.jpg)

## Why Use Blockchain?

1. **Data Integrity**: Ensures that records cannot be secretly modified.
2. **Auditability**: Complete history of all transactions is preserved.
3. **Trust**: Eliminates the need for a trusted intermediary.
4. **Security**: Cryptographic hashing makes the data tamper-evident.
5. **Transparency**: All participants have access to the same ledger.

## How to Use It

### Basic Setup

Import the required classes:

```javascript
import Blockchain from './Blockchain';
import BlockchainTransaction from './BlockchainTransaction';
```

### Creating a Blockchain

Create a new blockchain instance with a specified difficulty level, e.g. 3:

```javascript
const blockchain = new Blockchain(3);
```

**Constructor Parameter**:

- `difficulty` (number, default: 2): The number of leading zeros required in a block's hash during mining. Higher number means higher difficulty.

### Adding Transactions

Create transactions and add them to the pending transactions pool:

```javascript
const transaction1 = new BlockchainTransaction({
from: 'Alice',
to: 'Bob',
amount: 50,
description: 'Payment for services',
});
blockchain.addTransaction(transaction1);

const transaction2 = new BlockchainTransaction({
from: 'Bob',
to: 'Charlie',
amount: 25,
});
blockchain.addTransaction(transaction2);
```

**Transaction Class Properties**:

- `from`: The sender's address
- `to`: The recipient's address
- `amount`: The transaction amount (must be positive)
- `description`: Optional description of the transaction

### Mining Blocks

Mine pending transactions into a new block:

```javascript
const minedBlock = blockchain.minePendingTransactions('Miner1');
```

### Block Structure

Each block contains:

```javascript
index; // Position in blockchain
timestamp; // Creation time in ISO format
data; // Array of transactions in this block
previousHash; // Hash of the previous block
hash; // Current block's hash
nonce; // Number used in proof-of-work calculation
```

### Validating the Blockchain

Verify the integrity of the entire blockchain:

```javascript
if (blockchain.isChainValid()) {
// Blockchain is valid
} else {
// Blockchain has been tampered with!
}
```

### Checking Balances

Get the balance of an address by analyzing all transactions:

```javascript
const aliceBalance = blockchain.getBalance('Alice');
console.log(aliceBalance); // 10
```

### Retrieving Transactions

Find all transactions involving a specific address:

```javascript
const aliceTransactions = blockchain.getTransactionsForAddress('Alice');
console.log(aliceTransactions)
// [
// { block: 1, transaction: {...} },
// { block: 2, transaction: {...} }
// ]
```

### Getting Latest Block

Access the most recent block in the chain:

```javascript
const latestBlock = blockchain.getLatestBlock();
```

## Complete Example

```javascript
import Blockchain from './Blockchain.js';
import BlockchainTransaction from './BlockchainTransaction.js';

const blockchain = new Blockchain(2);

blockchain.addTransaction(new BlockchainTransaction({
from: 'Alice',
to: 'Bob',
amount: 30,
}));
blockchain.addTransaction(new BlockchainTransaction({
from: 'Bob',
to: 'Charlie',
amount: 15,
}));

blockchain.minePendingTransactions('Miner1');

blockchain.addTransaction(new BlockchainTransaction({
from: 'Charlie',
to: 'Alice',
amount: 10,
}));

blockchain.minePendingTransactions('Miner2');
```

## Implementation Details

### Mining and Difficulty

The `mineBlock(difficulty)` method in BlockchainBlock implements proof-of-work:

- It increments the `nonce` value until the resulting `hash` has the required number of leading zeros.
- Higher difficulty values require exponentially more computational work.
- This mechanism secures the blockchain by making it computationally expensive to tamper with blocks.

### Chain Validation

The `isChainValid()` method ensures:

- Each block's hash matches its calculated hash (prevents tampering).
- Each block's `previousHash` matches the previous block's `hash` (maintains chain integrity).
- The entire chain is unbroken.

### Pending Transactions

Transactions are held in `pendingTransactions` until they are included in a mined block. This allows:

- Multiple transactions to be batched together.
- Efficient block creation.
- Flexibility in mining timing.

## Security Considerations

- **Difficulty**: Increase `difficulty` for higher security (but slower mining).
- **Validation**: Always call `isChainValid()` before trusting the blockchain.
- **Address Verification**: This implementation doesn't include cryptographic signatures. In production, use digital signatures to verify transaction authenticity.
- **Double Spending**: Implement balance checks before accepting transactions in production systems.

## References

This documentation draws from fundamental blockchain concepts and implementation details. Key sources include:

- **Bitcoin Whitepaper**: Nakamoto, S. (2008). "Bitcoin: A Peer-to-Peer Electronic Cash System." Available at: https://bitcoin.org/bitcoin.pdf
- **Mastering Bitcoin**: Antonopoulos, A. M. (2017). "Mastering Bitcoin: Programming the Open Blockchain." O'Reilly Media.
- **Blockchain Technology Overview**: Swan, M. (2015). "Blockchain: Blueprint for a New Economy." O'Reilly Media.
- **Ethereum Documentation**: https://ethereum.org/en/developers/docs/
- **Wikipedia - Blockchain**: https://en.wikipedia.org/wiki/Blockchain
108 changes: 108 additions & 0 deletions src/data-structures/blockchain/__test__/Blockchain.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import Blockchain from '../Blockchain';
import BlockchainTransaction from '../BlockchainTransaction';

describe('Blockchain', () => {
it('should initialize with a mined genesis block', () => {
const blockchain = new Blockchain(1);
const [genesisBlock] = blockchain.chain;
expect(blockchain.difficulty).toBe(1);
expect(blockchain.chain).toHaveLength(1);
expect(blockchain.balances).toEqual(new Map());
expect(blockchain.transactionsByAddress).toEqual(new Map());
expect(genesisBlock.index).toBe(0);
expect(genesisBlock.data).toBe('Genesis Block');
expect(genesisBlock.previousHash).toBe('0');
expect(genesisBlock.hash.startsWith('0')).toBe(true);
expect(blockchain.getLatestBlock()).toBe(genesisBlock);
});

it('should validate and reject pending transactions based on required fields and amount', () => {
const blockchain = new Blockchain(0);
const validTransaction = new BlockchainTransaction({
from: 'alice',
to: 'bob',
amount: 15,
description: 'Invoice',
});
const invalidTransaction1 = new BlockchainTransaction({
from: '',
to: 'bob',
amount: 15,
});
const invalidTransaction2 = new BlockchainTransaction({
from: 'alice',
to: '',
amount: 15,
});
const invalidTransaction3 = new BlockchainTransaction({
from: 'alice',
to: 'bob',
amount: 0,
});
const invalidTransaction4 = new BlockchainTransaction({
from: 'alice',
to: 'bob',
amount: -5,
});
expect(blockchain.addTransaction(validTransaction)).toBe(true);
expect(blockchain.addTransaction(invalidTransaction1)).toBe(false);
expect(blockchain.addTransaction(invalidTransaction2)).toBe(false);
expect(blockchain.addTransaction(invalidTransaction3)).toBe(false);
expect(blockchain.addTransaction(invalidTransaction4)).toBe(false);
expect(blockchain.pendingTransactions).toEqual([validTransaction]);
expect(blockchain.getBalance('alice')).toBe(-15);
expect(blockchain.getBalance('bob')).toBe(15);
expect(blockchain.getTransactionsForAddress('alice')).toEqual([]);
blockchain.minePendingTransactions('miner-address');
expect(blockchain.getTransactionsForAddress('alice')).toEqual([
{ block: 1, transaction: validTransaction },
]);
});

it('should mine pending transactions, reward miner, and update balances and transaction history', () => {
const blockchain = new Blockchain(1);
const firstTransaction = new BlockchainTransaction({
from: 'alice',
to: 'bob',
amount: 25,
description: 'Payment',
});
const secondTransaction = new BlockchainTransaction({
from: 'bob',
to: 'carol',
amount: 5,
description: 'Refund',
});
const minerAddress = 'miner-address';
blockchain.addTransaction(firstTransaction);
blockchain.addTransaction(secondTransaction);
const minedBlock = blockchain.minePendingTransactions(minerAddress);
expect(blockchain.chain).toHaveLength(2);
expect(minedBlock.index).toBe(1);
expect(minedBlock.previousHash).toBe(blockchain.chain[0].hash);
expect(minedBlock.hash.startsWith('0')).toBe(true);
expect(minedBlock.data).toEqual([
firstTransaction,
secondTransaction,
// miner reward transaction
new BlockchainTransaction({
from: 'System',
to: minerAddress,
amount: 10,
description: 'Mining Reward',
}),
]);
expect(blockchain.pendingTransactions).toEqual([]);
expect(blockchain.getBalance('alice')).toBe(-25);
expect(blockchain.getBalance('bob')).toBe(20);
expect(blockchain.getBalance('carol')).toBe(5);
expect(blockchain.getBalance(minerAddress)).toBe(10);
expect(blockchain.getTransactionsForAddress('bob')).toEqual([
{ block: 1, transaction: firstTransaction },
{ block: 1, transaction: secondTransaction },
]);
expect(blockchain.isChainValid()).toBe(true);
minedBlock.data[0].amount = 100;
expect(blockchain.isChainValid()).toBe(false);
});
});
72 changes: 72 additions & 0 deletions src/data-structures/blockchain/__test__/BlockchainBlock.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import crypto from 'crypto';
import Block from '../BlockchainBlock';
import BlockchainTransaction from '../BlockchainTransaction';

describe('BlockchainBlock', () => {
it('should create block with deterministic hash from its contents', () => {
const transactions = [
new BlockchainTransaction({
from: 'alice',
to: 'bob',
amount: 25,
description: 'Rent',
}),
];
const timestamp = '2026-01-01T12:00:00.000Z';
const blockParams = {
index: 1,
timestamp,
data: transactions,
previousHash: 'previous-hash',
nonce: 7,
};
const block = new Block(blockParams);
const expectedHash = crypto
.createHash('sha256')
.update(JSON.stringify(blockParams))
.digest('hex');
expect(block.index).toBe(1);
expect(block.timestamp).toBe(timestamp);
expect(block.data).toBe(transactions);
expect(block.previousHash).toBe('previous-hash');
expect(block.nonce).toBe(7);
expect(block.hash).toBe(expectedHash);
});

it('should use current ISO timestamp when timestamp is not provided', () => {
const timestamp = '2026-01-01T12:00:00.000Z';
jest.useFakeTimers().setSystemTime(new Date(timestamp));
const block = new Block({
index: 2,
data: [
new BlockchainTransaction({
from: 'alice',
to: 'bob',
amount: 10,
}),
],
previousHash: 'previous-hash',
});
expect(block.timestamp).toBe(timestamp);
jest.useRealTimers();
});

it('should mine block until hash satisfies the requested difficulty', () => {
const block = new Block({
index: 3,
timestamp: '2026-05-31T12:00:00.000Z',
data: [
new BlockchainTransaction({
from: 'miner',
to: 'alice',
amount: 5,
}),
],
previousHash: 'previous-hash',
});
block.mineBlock(2);
expect(block.hash.startsWith('00')).toBe(true);
expect(block.nonce).toBeGreaterThan(0);
expect(block.hash).toBe(block.calculateHash());
});
});
Loading