Skip to content

Commit 12b9843

Browse files
committed
adds blockchain tests & README
1 parent 1de48ef commit 12b9843

5 files changed

Lines changed: 414 additions & 0 deletions

File tree

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# Blockchain
2+
3+
## What is Blockchain?
4+
5+
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.
6+
7+
Key characteristics:
8+
9+
- **Immutability**: Once data is recorded, it cannot be altered without detection.
10+
- **Transparency**: All transactions are visible to participants.
11+
- **Decentralization**: No single point of control.
12+
- **Security**: Uses cryptographic hashing to secure data.
13+
14+
![Blockchain](./images/blockchain.jpg)
15+
16+
## Why Use Blockchain?
17+
18+
1. **Data Integrity**: Ensures that records cannot be secretly modified.
19+
2. **Auditability**: Complete history of all transactions is preserved.
20+
3. **Trust**: Eliminates the need for a trusted intermediary.
21+
4. **Security**: Cryptographic hashing makes the data tamper-evident.
22+
5. **Transparency**: All participants have access to the same ledger.
23+
24+
## How to Use It
25+
26+
### Basic Setup
27+
28+
Import the required classes:
29+
30+
```javascript
31+
import Blockchain from './Blockchain';
32+
import BlockchainTransaction from './BlockchainTransaction';
33+
```
34+
35+
### Creating a Blockchain
36+
37+
Create a new blockchain instance with a specified difficulty level, e.g. 3:
38+
39+
```javascript
40+
const blockchain = new Blockchain(3);
41+
```
42+
43+
**Constructor Parameter**:
44+
45+
- `difficulty` (number, default: 2): The number of leading zeros required in a block's hash during mining. Higher number means higher difficulty.
46+
47+
### Adding Transactions
48+
49+
Create transactions and add them to the pending transactions pool:
50+
51+
```javascript
52+
const transaction1 = new BlockchainTransaction({
53+
from: 'Alice',
54+
to: 'Bob',
55+
amount: 50,
56+
description: 'Payment for services',
57+
});
58+
blockchain.addTransaction(transaction1);
59+
60+
const transaction2 = new BlockchainTransaction({
61+
from: 'Bob',
62+
to: 'Charlie',
63+
amount: 25,
64+
});
65+
blockchain.addTransaction(transaction2);
66+
```
67+
68+
**Transaction Class Properties**:
69+
70+
- `from`: The sender's address
71+
- `to`: The recipient's address
72+
- `amount`: The transaction amount (must be positive)
73+
- `description`: Optional description of the transaction
74+
75+
### Mining Blocks
76+
77+
Mine pending transactions into a new block:
78+
79+
```javascript
80+
const minedBlock = blockchain.minePendingTransactions('Miner1');
81+
```
82+
83+
### Block Structure
84+
85+
Each block contains:
86+
87+
```javascript
88+
index; // Position in blockchain
89+
timestamp; // Creation time in ISO format
90+
data; // Array of transactions in this block
91+
previousHash; // Hash of the previous block
92+
hash; // Current block's hash
93+
nonce; // Number used in proof-of-work calculation
94+
```
95+
96+
### Validating the Blockchain
97+
98+
Verify the integrity of the entire blockchain:
99+
100+
```javascript
101+
if (blockchain.isChainValid()) {
102+
// Blockchain is valid
103+
} else {
104+
// Blockchain has been tampered with!
105+
}
106+
```
107+
108+
### Checking Balances
109+
110+
Get the balance of an address by analyzing all transactions:
111+
112+
```javascript
113+
const aliceBalance = blockchain.getBalance('Alice');
114+
console.log(aliceBalance); // 10
115+
```
116+
117+
### Retrieving Transactions
118+
119+
Find all transactions involving a specific address:
120+
121+
```javascript
122+
const aliceTransactions = blockchain.getTransactionsForAddress('Alice');
123+
console.log(aliceTransactions)
124+
// [
125+
// { block: 1, transaction: {...} },
126+
// { block: 2, transaction: {...} }
127+
// ]
128+
```
129+
130+
### Getting Latest Block
131+
132+
Access the most recent block in the chain:
133+
134+
```javascript
135+
const latestBlock = blockchain.getLatestBlock();
136+
```
137+
138+
## Complete Example
139+
140+
```javascript
141+
import Blockchain from './Blockchain.js';
142+
import BlockchainTransaction from './BlockchainTransaction.js';
143+
144+
const blockchain = new Blockchain(2);
145+
146+
blockchain.addTransaction(new BlockchainTransaction({
147+
from: 'Alice',
148+
to: 'Bob',
149+
amount: 30,
150+
}));
151+
blockchain.addTransaction(new BlockchainTransaction({
152+
from: 'Bob',
153+
to: 'Charlie',
154+
amount: 15,
155+
}));
156+
157+
blockchain.minePendingTransactions('Miner1');
158+
159+
blockchain.addTransaction(new BlockchainTransaction({
160+
from: 'Charlie',
161+
to: 'Alice',
162+
amount: 10,
163+
}));
164+
165+
blockchain.minePendingTransactions('Miner2');
166+
```
167+
168+
## Implementation Details
169+
170+
### Mining and Difficulty
171+
172+
The `mineBlock(difficulty)` method in BlockchainBlock implements proof-of-work:
173+
174+
- It increments the `nonce` value until the resulting `hash` has the required number of leading zeros.
175+
- Higher difficulty values require exponentially more computational work.
176+
- This mechanism secures the blockchain by making it computationally expensive to tamper with blocks.
177+
178+
### Chain Validation
179+
180+
The `isChainValid()` method ensures:
181+
182+
- Each block's hash matches its calculated hash (prevents tampering).
183+
- Each block's `previousHash` matches the previous block's `hash` (maintains chain integrity).
184+
- The entire chain is unbroken.
185+
186+
### Pending Transactions
187+
188+
Transactions are held in `pendingTransactions` until they are included in a mined block. This allows:
189+
190+
- Multiple transactions to be batched together.
191+
- Efficient block creation.
192+
- Flexibility in mining timing.
193+
194+
## Security Considerations
195+
196+
- **Difficulty**: Increase `difficulty` for higher security (but slower mining).
197+
- **Validation**: Always call `isChainValid()` before trusting the blockchain.
198+
- **Address Verification**: This implementation doesn't include cryptographic signatures. In production, use digital signatures to verify transaction authenticity.
199+
- **Double Spending**: Implement balance checks before accepting transactions in production systems.
200+
201+
## References
202+
203+
This documentation draws from fundamental blockchain concepts and implementation details. Key sources include:
204+
205+
- **Bitcoin Whitepaper**: Nakamoto, S. (2008). "Bitcoin: A Peer-to-Peer Electronic Cash System." Available at: https://bitcoin.org/bitcoin.pdf
206+
- **Mastering Bitcoin**: Antonopoulos, A. M. (2017). "Mastering Bitcoin: Programming the Open Blockchain." O'Reilly Media.
207+
- **Blockchain Technology Overview**: Swan, M. (2015). "Blockchain: Blueprint for a New Economy." O'Reilly Media.
208+
- **Ethereum Documentation**: https://ethereum.org/en/developers/docs/
209+
- **Wikipedia - Blockchain**: https://en.wikipedia.org/wiki/Blockchain
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import Blockchain from '../Blockchain';
2+
import BlockchainTransaction from '../BlockchainTransaction';
3+
4+
describe('Blockchain', () => {
5+
it('should initialize with a mined genesis block', () => {
6+
const blockchain = new Blockchain(1);
7+
const [genesisBlock] = blockchain.chain;
8+
expect(blockchain.difficulty).toBe(1);
9+
expect(blockchain.chain).toHaveLength(1);
10+
expect(blockchain.balances).toEqual(new Map());
11+
expect(blockchain.transactionsByAddress).toEqual(new Map());
12+
expect(genesisBlock.index).toBe(0);
13+
expect(genesisBlock.data).toBe('Genesis Block');
14+
expect(genesisBlock.previousHash).toBe('0');
15+
expect(genesisBlock.hash.startsWith('0')).toBe(true);
16+
expect(blockchain.getLatestBlock()).toBe(genesisBlock);
17+
});
18+
19+
it('should validate and reject pending transactions based on required fields and amount', () => {
20+
const blockchain = new Blockchain(0);
21+
const validTransaction = new BlockchainTransaction({
22+
from: 'alice',
23+
to: 'bob',
24+
amount: 15,
25+
description: 'Invoice',
26+
});
27+
const invalidTransaction1 = new BlockchainTransaction({
28+
from: '',
29+
to: 'bob',
30+
amount: 15,
31+
});
32+
const invalidTransaction2 = new BlockchainTransaction({
33+
from: 'alice',
34+
to: '',
35+
amount: 15,
36+
});
37+
const invalidTransaction3 = new BlockchainTransaction({
38+
from: 'alice',
39+
to: 'bob',
40+
amount: 0,
41+
});
42+
const invalidTransaction4 = new BlockchainTransaction({
43+
from: 'alice',
44+
to: 'bob',
45+
amount: -5,
46+
});
47+
expect(blockchain.addTransaction(validTransaction)).toBe(true);
48+
expect(blockchain.addTransaction(invalidTransaction1)).toBe(false);
49+
expect(blockchain.addTransaction(invalidTransaction2)).toBe(false);
50+
expect(blockchain.addTransaction(invalidTransaction3)).toBe(false);
51+
expect(blockchain.addTransaction(invalidTransaction4)).toBe(false);
52+
expect(blockchain.pendingTransactions).toEqual([validTransaction]);
53+
expect(blockchain.getBalance('alice')).toBe(-15);
54+
expect(blockchain.getBalance('bob')).toBe(15);
55+
expect(blockchain.getTransactionsForAddress('alice')).toEqual([]);
56+
blockchain.minePendingTransactions('miner-address');
57+
expect(blockchain.getTransactionsForAddress('alice')).toEqual([
58+
{ block: 1, transaction: validTransaction },
59+
]);
60+
});
61+
62+
it('should mine pending transactions, reward miner, and update balances and transaction history', () => {
63+
const blockchain = new Blockchain(1);
64+
const firstTransaction = new BlockchainTransaction({
65+
from: 'alice',
66+
to: 'bob',
67+
amount: 25,
68+
description: 'Payment',
69+
});
70+
const secondTransaction = new BlockchainTransaction({
71+
from: 'bob',
72+
to: 'carol',
73+
amount: 5,
74+
description: 'Refund',
75+
});
76+
const minerAddress = 'miner-address';
77+
blockchain.addTransaction(firstTransaction);
78+
blockchain.addTransaction(secondTransaction);
79+
const minedBlock = blockchain.minePendingTransactions(minerAddress);
80+
expect(blockchain.chain).toHaveLength(2);
81+
expect(minedBlock.index).toBe(1);
82+
expect(minedBlock.previousHash).toBe(blockchain.chain[0].hash);
83+
expect(minedBlock.hash.startsWith('0')).toBe(true);
84+
expect(minedBlock.data).toEqual([
85+
firstTransaction,
86+
secondTransaction,
87+
// miner reward transaction
88+
new BlockchainTransaction({
89+
from: 'System',
90+
to: minerAddress,
91+
amount: 10,
92+
description: 'Mining Reward',
93+
}),
94+
]);
95+
expect(blockchain.pendingTransactions).toEqual([]);
96+
expect(blockchain.getBalance('alice')).toBe(-25);
97+
expect(blockchain.getBalance('bob')).toBe(20);
98+
expect(blockchain.getBalance('carol')).toBe(5);
99+
expect(blockchain.getBalance(minerAddress)).toBe(10);
100+
expect(blockchain.getTransactionsForAddress('bob')).toEqual([
101+
{ block: 1, transaction: firstTransaction },
102+
{ block: 1, transaction: secondTransaction },
103+
]);
104+
expect(blockchain.isChainValid()).toBe(true);
105+
minedBlock.data[0].amount = 100;
106+
expect(blockchain.isChainValid()).toBe(false);
107+
});
108+
});
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import crypto from 'crypto';
2+
import Block from '../BlockchainBlock';
3+
import BlockchainTransaction from '../BlockchainTransaction';
4+
5+
describe('BlockchainBlock', () => {
6+
it('should create block with deterministic hash from its contents', () => {
7+
const transactions = [
8+
new BlockchainTransaction({
9+
from: 'alice',
10+
to: 'bob',
11+
amount: 25,
12+
description: 'Rent',
13+
}),
14+
];
15+
const timestamp = '2026-01-01T12:00:00.000Z';
16+
const blockParams = {
17+
index: 1,
18+
timestamp,
19+
data: transactions,
20+
previousHash: 'previous-hash',
21+
nonce: 7,
22+
};
23+
const block = new Block(blockParams);
24+
const expectedHash = crypto
25+
.createHash('sha256')
26+
.update(JSON.stringify(blockParams))
27+
.digest('hex');
28+
expect(block.index).toBe(1);
29+
expect(block.timestamp).toBe(timestamp);
30+
expect(block.data).toBe(transactions);
31+
expect(block.previousHash).toBe('previous-hash');
32+
expect(block.nonce).toBe(7);
33+
expect(block.hash).toBe(expectedHash);
34+
});
35+
36+
it('should use current ISO timestamp when timestamp is not provided', () => {
37+
const timestamp = '2026-01-01T12:00:00.000Z';
38+
jest.useFakeTimers().setSystemTime(new Date(timestamp));
39+
const block = new Block({
40+
index: 2,
41+
data: [
42+
new BlockchainTransaction({
43+
from: 'alice',
44+
to: 'bob',
45+
amount: 10,
46+
}),
47+
],
48+
previousHash: 'previous-hash',
49+
});
50+
expect(block.timestamp).toBe(timestamp);
51+
jest.useRealTimers();
52+
});
53+
54+
it('should mine block until hash satisfies the requested difficulty', () => {
55+
const block = new Block({
56+
index: 3,
57+
timestamp: '2026-05-31T12:00:00.000Z',
58+
data: [
59+
new BlockchainTransaction({
60+
from: 'miner',
61+
to: 'alice',
62+
amount: 5,
63+
}),
64+
],
65+
previousHash: 'previous-hash',
66+
});
67+
block.mineBlock(2);
68+
expect(block.hash.startsWith('00')).toBe(true);
69+
expect(block.nonce).toBeGreaterThan(0);
70+
expect(block.hash).toBe(block.calculateHash());
71+
});
72+
});

0 commit comments

Comments
 (0)