Skip to content

Instantly share code, notes, and snippets.

@AliSawari
Last active May 14, 2018 15:49
Show Gist options
  • Save AliSawari/a5e0d0058a279d92cd5db467a8e5501f to your computer and use it in GitHub Desktop.
Save AliSawari/a5e0d0058a279d92cd5db467a8e5501f to your computer and use it in GitHub Desktop.
A simple BlockChain example via Nodejs
// A simple BlockChain example via Nodejs
// this example shows the core functionality of a BlockChain system and how it works
// want to add more options? here's the GitHub rep: https://github.com/AliSawari/SimpleBlockChain
// THIS EXAMPLE IS INSPIRED BY : youtu.be/zVqczFZr124
// requiring Hash function
const {createHmac} = require('crypto');
// simplify some functions
function str(val){
return JSON.stringify(val, null, 2);
}
// the block class is used to init a new Block
// index is the position of the block in the chain
// data is the user's secret input of data
// timeStamp is the time that the block has been created
// prevHash is the hash of the previous block
// hash is the hash calculated by the calcHash method
// nonce is the random number which is being used in a hash function in order ->
// to reach a certain amount of zeros in the beginning of the hash (AKA : Proof of Work)
class Block {
constructor(index, data, prevHash){
this.index = index;
this.data = data;
this.timeStamp = Date.now();
this.prevHash = prevHash;
this.nonce = 0;
this.hash = this.calcHash();
}
calcHash(){
// this method is used to take some values, put them in an object then stringify that object
// toBeHashed value is the stringified type of our data and it'll be passed to the hash function
let {index, data, timeStamp, prevHash, nonce} = this;
let simple = {index, data, timeStamp, prevHash, nonce};
let toBeHashed = str(simple);
return createHmac('sha256', toBeHashed).digest('hex');
}
mine(diff){
// this method makes sure that for each defined Difficulty there is zeros in beginning of the hash
// this method is the implementation of Proof of Work. which makes mining each block take longer time
// you see here nonce will be incremented by one. that will affect the output of the hash function untill
// there are enough zeros to pass the if statement
// while(this.hash.substring(0, diff) !== Array(diff + 1).join('0')){
// this.nonce = this.nonce + 1;
// this.hash = this.calcHash();
// }
if(this.hash.substring(0, diff) === Array(diff + 1).join('0')){
return console.log(`\nBlock Mined: ${this.hash}`);
} else {
this.nonce = this.nonce + 1;
this.hash = this.calcHash();
return this.mine(diff);
}
}
}
// this is the BlockChain class. used to init a new BlockChain
// notice how the BlockChain is inited by a Genesis block. this block is the first block
// thats why it has index of 0 and previous Hash of 0
// chain is the array that holds the block objects
// diff is the current Difficulty of the BlockChain. it will be passed to the mine method in the block
// to define the amount of zeros in the beginning of a hash
class BlockChain {
constructor(){
this.chain = [new Block(0, "Genesis Block", 0)];
this.diff = 1;
}
getLatestBlock(){
// returns the latest block
return this.chain[this.chain.length - 1];
}
addBlock(data){
// takes the users input data and pass it to a new block
// this.chain.length = index + 1
// get Latest Block's hash
// mines the block with the specified Difficulty
// adds the block to the chain
// updates the Difficulty
let newBlock = new Block(this.chain.length, data, this.getLatestBlock().hash);
newBlock.mine(this.diff);
this.chain.push(newBlock);
this.updateDiff();
}
updateDiff(){
// this method gets called each time a new block is added to the chain
// it increases the Difficulty by the amount of the blocks
let n = this.chain.length;
if(n >= 1 && n < 20){
this.diff = 1;
}
if(n >= 20 && n < 50){
this.diff = 2;
}
if(n >= 20 && n < 50){
this.diff = 3;
}
if(n >= 50 && n < 100){
this.diff = 4;
}
if(n >= 100 && n < 200){
this.diff = 5;
}
if(n >= 200 && n < 500){
this.diff = 6;
}
}
info(){
// returns some information about the BlockChain
console.log(`\nBlockChain blocks: ${this.chain.length}`);
console.log(`\nBlockChain Difficulty: ${this.diff}`);
console.log('\nChain: \n', this.chain);
}
isChainValid(){
// this method makes sure everything is perfectly legal and correct
let rt = true;
for(let x = 1; x < this.chain.length; x++){
let current = this.chain[x];
let prev = this.chain[x - 1];
// each block has a correct value of hash based on it's properties
if(current.hash !== current.calcHash()){
rt = false;
return rt;
}
// each block points to a previous block using hashes
if(current.prevHash !== prev.hash){
rt = false;
return rt;
}
}
return rt;
}
}
// EXAMPLES :
// my new BlockChain :)
var AliCoin = new BlockChain();
// adding some blocks
AliCoin.addBlock({
from: 'Ali',
to: 'World',
amount: 250
});
AliCoin.addBlock({
from: 'Peter',
to: 'John',
amount: 590
});
AliCoin.addBlock({
from: 'Ali',
to: 'Peter',
amount: 900
});
AliCoin.addBlock({
from: 'John',
to: 'World',
amount: 120
});
// getting information
AliCoin.info();
// making sure the BlockChain is correct
// using timeout to make sure all blocks are mined
setTimeout(() => {
console.log("Is our BlockChain Valid? ", AliCoin.isChainValid())
}, 500);
// add a block each second
// setInterval(() => {
// AliCoin.addBlock({
// amount: 3
// });
// },1000);
// Go ahead and change the data of one block and see if the chain
// is still Valid or not
// like this :
// aww Peter is so greedy! he wants a 100000 AliCoin :))
/*
AliCoin.chain[3].data = {
from: 'Ali',
to: 'Peter',
amount: 100000
}
console.log("Is our BlockChain Valid? ", AliCoin.isChainValid())
*/
// the code above will resolve false! because the current hash is different than the
// new recalculated hash. that means data was changed!
// even if he recalculate his hash like this:
/*
AliCoin.chain[3].hash = AliCoin.chain[3].calcHash();
console.log("Is our BlockChain Valid? ", AliCoin.isChainValid())
*/
// its still unvalid :^ because when we recalculate our hash with different values
// the next block will not point to our regenerated fake block and will break up the chain :D
// TEST IT OUT YOURSELF AND LET ME KNOW WHAT YOU THINK IN THE COMMENTS
@AliSawari
Copy link
Author

RangeError: Maximum call stack size exceeded

I have to fix this one 😄

its from the mine method in the Block class, its a recursive function
so when the difficulty gets too high and the possibilities for hash values increase massively
it will be called over and over again causing a Maximum call stack Error
any Ideas? 🤔

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