v6 initial commit, maybe more to follow

This commit is contained in:
Josip Milovac 2022-11-22 13:30:19 +11:00
parent 7f91be86c0
commit d6a32870bc
34 changed files with 16875 additions and 0 deletions

BIN
blockchain/.DS_Store vendored Normal file

Binary file not shown.

58
blockchain/block.js Normal file
View file

@ -0,0 +1,58 @@
const ChainUtil = require('../chain-util');
const { DIFFICULTY, MINE_RATE } = require('../config');
class Block {
constructor(timestamp,BlockNum, lastHash, hash, data, nonce,
difficulty) {
this.timestamp = timestamp;
this.BlockNum = BlockNum;
this.lastHash = lastHash;
this.hash = hash;
this.data = data;
this.nonce = nonce;
this.difficulty = difficulty || DIFFICULTY;
}
toString() {
return `Block -
Timestamp : ${this.timestamp}
BlockNum : ${this.BlockNum}
Last Hash : ${this.lastHash.substring(0, 10)}
Hash : ${this.hash.substring(0, 10)}
Nonce : ${this.nonce}
Difficulty: ${this.difficulty}
Data : ${this.data[0].length}`;
}
static genesis() {
return new this('Genesis-time',0, '-----', 'First-Hash',
[], 0, DIFFICULTY);
}
static mineBlock(lastBlock, data) {
let hash, timestamp;
const lastHash = lastBlock.hash;
let { difficulty } = lastBlock;
let nonce = 0;
let BlockNum = lastBlock.BlockNum + 1;
do {
nonce++;
timestamp = Date.now();
difficulty = Block.adjustDifficulty(lastBlock, timestamp);
hash = Block.hash(timestamp, lastHash, data, nonce, difficulty);
} while (hash.substring(0, difficulty) !== '0'.repeat(difficulty));
return new this(timestamp,BlockNum, lastHash, hash, data, nonce,
difficulty);}
static hash(timestamp,lastHash, data, nonce, difficulty) {
return ChainUtil.hash(`${timestamp}${lastHash}${data}${nonce}
${difficulty}`).toString();
}
static blockHash(block) {
const { timestamp, lastHash, data, nonce, difficulty } = block;
return Block.hash(timestamp, lastHash, data, nonce, difficulty);
}
static adjustDifficulty(lastBlock, currentTime) {
let { difficulty } = lastBlock;
difficulty = lastBlock.timestamp + MINE_RATE > currentTime ?
difficulty + 1 : difficulty - 1;
return difficulty;
}
}
module.exports = Block;

34
blockchain/block.test.js Normal file
View file

@ -0,0 +1,34 @@
const Block = require('./block');
describe('Block', () => {
let data, lastBlock, block;
beforeEach(() => {
data = 'bar';
lastBlock = Block.genesis();
block = Block.mineBlock(lastBlock, data);
});
it('sets the `data` to match the input', () => {
expect(block.data).toEqual(data);
});
it('sets the `lastHash` to match the hash of the last block', () => {
expect(block.lastHash).toEqual(lastBlock.hash);
});
it('generates a hash that matches the difficulty', () => {
expect(block.hash.substring(0, block.difficulty))
.toEqual('0'.repeat(block.difficulty));
});
it('lowers the difficulty for slowly mined blocks', () => {
expect(Block.adjustDifficulty(block, block.timestamp+360000))
.toEqual(block.difficulty-1);
});
it('raises the difficulty for quickly mined blocks', () => {
expect(Block.adjustDifficulty(block, block.timestamp+1))
.toEqual(block.difficulty+1);
});
});

45
blockchain/index.js Normal file
View file

@ -0,0 +1,45 @@
const Block = require('./block');
class Blockchain {
constructor() {
this.chain = [Block.genesis()];
}
addBlock(data) {
const block = Block.mineBlock(this.chain[this.chain.length-1], data);
this.chain.push(block);
return block;
}
isValidChain(chain) {
if(JSON.stringify(chain[0]) !== JSON.stringify(Block.genesis())) return false;
for (let i=1; i<chain.length; i++) {
const block = chain[i];
const lastBlock = chain[i-1];
if (block.lastHash !== lastBlock.hash ||
block.hash !== Block.blockHash(block)) {
return false;
}
}
return true;
}
replaceChain(newChain) {
if (newChain.length <= this.chain.length) {
console.log('Received chain is not longer than the current chain.');
return;
} else if (!this.isValidChain(newChain)) {
console.log('The received chain is not valid.');
return;
}
console.log('Replacing blockchain with the new chain.');
this.chain = newChain;
}
}
module.exports = Blockchain;

55
blockchain/index.test.js Normal file
View file

@ -0,0 +1,55 @@
const Blockchain = require('./index');
const Block = require('./block');
describe('Blockchain', () => {
let bc, bc2;
beforeEach(() => {
bc = new Blockchain();
bc2 = new Blockchain();
});
it('starts with genesis block', () => {
expect(bc.chain[0]).toEqual(Block.genesis());
});
it('adds a new block', () => {
const data = 'foo';
bc.addBlock(data);
expect(bc.chain[bc.chain.length-1].data).toEqual(data);
});
it('validates a valid chain', () => {
bc2.addBlock('foo');
expect(bc.isValidChain(bc2.chain)).toBe(true);
});
it('invalidates a chain with a corrupt genesis block', () => {
bc2.chain[0].data = 'Bad data';
expect(bc.isValidChain(bc2.chain)).toBe(false);
});
it('invalidates a corrupt chain', () => {
bc2.addBlock('foo');
bc2.chain[1].data = 'Not foo';
expect(bc.isValidChain(bc2.chain)).toBe(false);
});
it('replaces the chain with a valid chain', () => {
bc2.addBlock('goo');
bc.replaceChain(bc2.chain);
expect(bc.chain).toEqual(bc2.chain);
});
it('does not replace the chain with one of less than or equal to length', () => {
bc.addBlock('foo');
bc.replaceChain(bc2.chain);
expect(bc.chain).not.toEqual(bc2.chain);
})
});