const Block = require("./Block.js"); const crypto = require("crypto");
class Blockchain { constructor() { this.blockchain = [Block.genesis]; this.difficulty = 3; }
get() { return this.blockchain; }
get latestBlock() { return this.blockchain[this.blockchain.length - 1]; }
isValidHashDifficulty(hash) { for (var i = 0; i < hash.length; i++) { if (hash[i] !== "0") { break; } } return i >= this.difficulty; }
calculateHashForBlock(block) { const { index, previousHash, timestamp, data, nonce } = block; return this.calculateHash( index, previousHash, timestamp, data, nonce ); }
calculateHash(index, previousHash, timestamp, data, nonce) { return crypto .createHash("sha256") .update(index + previousHash + timestamp + data + nonce) .digest("hex"); }
mine(data) { const newBlock = this.generateNextBlock(data); try { this.addBlock(newBlock); } catch(err) { throw err; } }
generateNextBlock(data) { const nextIndex = this.latestBlock.index + 1; const previousHash = this.latestBlock.hash; let timestamp = new Date().getTime(); let nonce = 0; let nextHash = this.calculateHash( nextIndex, previousHash, timestamp, data, nonce );
while (!this.isValidHashDifficulty(nextHash)) {
nonce = nonce + 1;
timestamp = new Date().getTime();
nextHash = this.calculateHash(
nextIndex,
previousHash,
timestamp,
data,
nonce
);
}
const nextBlock = new Block(
nextIndex,
previousHash,
timestamp,
data,
nextHash,
nonce
);
return nextBlock;
}
addBlock(newBlock) { if (this.isValidNextBlock(newBlock, this.latestBlock)) { this.blockchain.push(newBlock); } else { throw "Error: Invalid block"; } }
isValidNextBlock(nextBlock, previousBlock) { const nextBlockHash = this.calculateHashForBlock(nextBlock);
if (previousBlock.index + 1 !== nextBlock.index) {
return false;
} else if (previousBlock.hash !== nextBlock.previousHash) {
return false;
} else if (nextBlockHash !== nextBlock.hash) {
return false;
} else if (!this.isValidHashDifficulty(nextBlockHash)) {
return false;
} else {
return true;
}
}
isValidChain(chain) { if (JSON.stringify(chain[0]) !== JSON.stringify(Block.genesis)) { return false; }
const tempChain = [chain[0]];
for (let i = 1; i < chain.length; i = i + 1) {
if (this.isValidNextBlock(chain[i], tempChain[i - 1])) {
tempChain.push(chain[i]);
} else {
return false;
}
}
return true;
}
isChainLonger(chain) { return chain.length > this.blockchain.length; }
replaceChain(newChain) { if (this.isValidChain(newChain) && this.isChainLonger(newChain)) { this.blockchain = JSON.parse(JSON.stringify(newChain)); } else { throw "Error: invalid chain"; } } }
module.exports = Blockchain;