v6 initial commit, maybe more to follow
This commit is contained in:
parent
7f91be86c0
commit
d6a32870bc
34 changed files with 16875 additions and 0 deletions
BIN
blockchain/.DS_Store
vendored
Normal file
BIN
blockchain/.DS_Store
vendored
Normal file
Binary file not shown.
58
blockchain/block.js
Normal file
58
blockchain/block.js
Normal 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
34
blockchain/block.test.js
Normal 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
45
blockchain/index.js
Normal 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
55
blockchain/index.test.js
Normal 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);
|
||||
})
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue