Skip to content

Instantly share code, notes, and snippets.

@michielmulders
Created December 28, 2017 14:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save michielmulders/aee06173042c1472419a7123c8c3a915 to your computer and use it in GitHub Desktop.
Save michielmulders/aee06173042c1472419a7123c8c3a915 to your computer and use it in GitHub Desktop.
Build Your Own Blockchain with JavaScript
const SHA256 = require('crypto-js/sha256');
class Block {
constructor(index, timestamp, data, previousHash = '') {
this.index = index;
this.previousHash = previousHash;
this.timestamp = timestamp;
this.data = data;
this.hash = this.calculateHash();
this.nonce = 0;
}
calculateHash() {
return SHA256(this.index + this.previousHash + this.timestamp + JSON.stringify(this.data) + this.nonce).toString();
}
mineBlock(difficulty) {
while (this.hash.substring(0, difficulty) !== Array(difficulty + 1).join('0')) {
this.nonce++;
this.hash = this.calculateHash();
}
console.log('BLOCK MINED: ' + this.hash);
}
}
class Blockchain{
constructor() {
this.chain = [this.createGenesisBlock()];
this.difficulty = 1;
}
createGenesisBlock() {
return new Block(0, '01/01/2018', 'Genesis block', '0');
}
getLatestBlock() {
return this.chain[this.chain.length - 1];
}
getBlockByIndex(index) {
if (index >= this.chain.length) {
return 'Error: Index out of range! Could not find block.'
}
return this.chain[index];
}
addBlock(newBlock) {
newBlock.previousHash = this.getLatestBlock().hash;
newBlock.mineBlock(this.difficulty);
this.chain.push(newBlock);
}
isChainValid() {
for (let i = 1; i < this.chain.length; i++){
const currentBlock = this.chain[i];
const previousBlock = this.chain[i - 1];
if (currentBlock.hash !== currentBlock.calculateHash()) {
return false;
}
if (currentBlock.previousHash !== previousBlock.hash) {
return false;
}
}
return true;
}
}
let carChain = new Blockchain();
console.log('Mining block 1...');
carChain.addBlock(new Block(1, '01/01/2018', { owner: 'Ben', license: '1-45A-UD9' }));
console.log('Retrieve block 1: ', JSON.stringify(carChain.getBlockByIndex(1), null, 4))
console.log('\n\nMining block 2...');
carChain.addBlock(new Block(2, '01/01/2018', { owner: 'Alice', license: '3-6EY-KL0' }));
console.log('\nTry to change blockchain data: give myself a new car which actually belongs to Ben.')
carChain.chain[1].data = { owner: 'Michiel', license: '1-45A-UD9' };
console.log('Regenerating hash for changed block and adding it back...\n')
carChain.chain[1].hash = carChain.chain[1].calculateHash();
console.log('Blockchain valid? ' + carChain.isChainValid());
console.log('Print My Blockchain\n');
console.log(JSON.stringify(carChain, null, 4));
{
"name": "build-your-own-blockchain",
"main": "main.js",
"scripts": {
"start": "node main.js"
},
"author": "Michiel Mulders",
"dependencies": {
"crypto-js": "^3.1.9-1"
}
}
@mrharel
Copy link

mrharel commented Jan 8, 2018

Great post, thank you for sharing.
One question: what is the importance of mining, why do we need to have zeros at the beginning of the hash?

@michielmulders
Copy link
Author

Hi @mrharel , thanks!
We don't need to have zeros. For example ethereum uses '0x' as prefix and uses a much harder algorithm.
You can hard code whatever you want to use as a prefix.
Mining allows us to have public validation of the chain.

Side note: I'm more a fan of systems that use a Tangle / DAG / Hashgraph where mining is 'eliminated'.
The transaction proposer has to mine some other transactions first before his transaction will be verified.
Interesting read!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment