Skip to content

Commit

Permalink
Init commit
Browse files Browse the repository at this point in the history
  • Loading branch information
15Dkatz committed Nov 11, 2018
0 parents commit f7b29f1
Show file tree
Hide file tree
Showing 10 changed files with 5,689 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*node_modules*
*dist*
*.cache*
29 changes: 29 additions & 0 deletions block.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const { GENESIS_DATA } = require('./config');
const cryptoHash = require('./crypto-hash');

class Block {
constructor({ timestamp, lastHash, hash, data }) {
this.timestamp = timestamp;
this.lastHash = lastHash;
this.hash = hash;
this.data = data;
}

static genesis() {
return new this(GENESIS_DATA);
}

static mineBlock({ lastBlock, data }) {
const timestamp = Date.now();
const lastHash = lastBlock.hash;

return new this({
timestamp,
lastHash,
data,
hash: cryptoHash(timestamp, lastHash, data)
});
}
}

module.exports = Block;
57 changes: 57 additions & 0 deletions block.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
const Block = require('./block');
const { GENESIS_DATA } = require('./config');
const cryptoHash = require('./crypto-hash');

describe('Block', () => {
const timestamp = 'a-date';
const lastHash = 'foo-hash';
const hash = 'bar-hash';
const data = ['blockchain', 'data'];
const block = new Block({ timestamp, lastHash, hash, data });

it('has a timestamp, lastHash, hash, and data property', () => {
expect(block.timestamp).toEqual(timestamp);
expect(block.lastHash).toEqual(lastHash);
expect(block.hash).toEqual(hash);
expect(block.data).toEqual(data);
});

describe('genesis()', () => {
const genesisBlock = Block.genesis();

it('returns a Block instance', () => {
expect(genesisBlock instanceof Block).toBe(true);
});

it('returns the genesis data', () => {
expect(genesisBlock).toEqual(GENESIS_DATA);
});
});

describe('mineBlock()', () => {
const lastBlock = Block.genesis();
const data = 'mined data';
const minedBlock = Block.mineBlock({ lastBlock, data });

it('returns a Block instance', () => {
expect(minedBlock instanceof Block).toBe(true);
});

it('sets the `lastHash` to be the `hash` of the lastBlock', () => {
expect(minedBlock.lastHash).toEqual(lastBlock.hash);
});

it('sets the `data`', () => {
expect(minedBlock.data).toEqual(data);
});

it('sets a `timestamp`', () => {
expect(minedBlock.timestamp).not.toEqual(undefined);
});

it('creates a SHA-256 `hash` based on the proper inputs', () => {
expect(minedBlock.hash)
.toEqual(cryptoHash(minedBlock.timestamp, lastBlock.hash, data));
});
});
});
18 changes: 18 additions & 0 deletions blockchain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const Block = require('./block');

class Blockchain {
constructor() {
this.chain = [Block.genesis()];
}

addBlock({ data }) {
const newBlock = Block.mineBlock({
lastBlock: this.chain[this.chain.length-1],
data
});

this.chain.push(newBlock);
}
}

module.exports = Blockchain;
65 changes: 65 additions & 0 deletions blockchain.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const Blockchain = require('./blockchain');
const Block = require('./block');

describe('Blockchain', () => {
let blockchain;

beforeEach(() => {
blockchain = new Blockchain();
});

it('contains a `chain` Array instance', () => {
expect(blockchain.chain instanceof Array).toBe(true);
});

it('starts with the genesis block', () => {
expect(blockchain.chain[0]).toEqual(Block.genesis());
});

it('adds a new block to the chain', () => {
const newData = 'foo bar';
blockchain.addBlock({ data: newData });

expect(blockchain.chain[blockchain.chain.length-1].data).toEqual(newData);
});

describe('isValidChain()', () => {
describe('when the chain does not start with the genesis block', () => {
it('returns false', () => {
blockchain.chain[0] = { data: 'fake-genesis' };

expect(Blockchain.isValidChain(blockchain.chain)).toBe(false);
});
});

describe('when the chain starts with the genesis block and has multiple blocks', () => {
beforeEach(() => {
blockchain.addBlock({ data: 'Bears' });
blockchain.addBlock({ data: 'Beets' });
blockchain.addBlock({ data: 'Battlestar Galactica' });
});

describe('and a lastHash reference has changed', () => {
it('returns false', () => {
blockchain.chain[2].lastHash = 'broken-lastHash';

expect(Blockchain.isValidChain(blockchain.chain)).toBe(false);
});
});

describe('and the chain contains a block with an invalid field', () => {
it('returns false', () => {
blockchain.chain[2].data = 'some-bad-and-evil-data';

expect(Blockchain.isValidChain(blockchain.chain)).toBe(false);
});
});

describe('and the chain does not contain any invalid blocks', () => {
it('returns true', () => {
expect(Blockchain.isValidChain(blockchain.chain)).toBe(true);
});
});
});
});
});
8 changes: 8 additions & 0 deletions config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const GENESIS_DATA = {
timestamp: 1,
lastHash: '-----',
hash: 'hash-one',
data: []
};

module.exports = { GENESIS_DATA };
11 changes: 11 additions & 0 deletions crypto-hash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const crypto = require('crypto');

const cryptoHash = (...inputs) => {
const hash = crypto.createHash('sha256');

hash.update(inputs.sort().join(' '));

return hash.digest('hex');
};

module.exports = cryptoHash;
13 changes: 13 additions & 0 deletions crypto-hash.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const cryptoHash = require('./crypto-hash');

describe('cryptoHash()', () => {
it('generates a SHA-256 hashed output', () => {
expect(cryptoHash('foo'))
.toEqual('2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae');
});

it('produces the same hash with the same input arguments in any order', () => {
expect(cryptoHash('one', 'two', 'three'))
.toEqual(cryptoHash('three', 'one', 'two'));
});
});
Loading

0 comments on commit f7b29f1

Please sign in to comment.