├── .gitignore ├── index.js ├── package-lock.json ├── package.json └── src ├── block.js └── blockchain.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .vscode 4 | *.log -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const Blockchain = require("./src/blockchain"); 2 | const Block = require("./src/block"); 3 | 4 | async function run() { 5 | const blockchain = await new Blockchain(); 6 | const block1 = new Block({ data: "Block #1" }); 7 | await blockchain.addBlock(block1); 8 | const block2 = new Block({ data: "Block #2" }); 9 | await blockchain.addBlock(block2); 10 | const block3 = new Block({ data: "Block #3" }); 11 | await blockchain.addBlock(block3); 12 | 13 | blockchain.print(); 14 | } 15 | 16 | run(); 17 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yt-blockchain", 3 | "version": "0.0.1", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "yt-blockchain", 9 | "version": "0.0.1", 10 | "license": "MIT", 11 | "dependencies": { 12 | "crypto-js": "^4.1.1", 13 | "hex2ascii": "^0.0.3" 14 | } 15 | }, 16 | "node_modules/crypto-js": { 17 | "version": "4.1.1", 18 | "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", 19 | "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" 20 | }, 21 | "node_modules/hex2ascii": { 22 | "version": "0.0.3", 23 | "resolved": "https://registry.npmjs.org/hex2ascii/-/hex2ascii-0.0.3.tgz", 24 | "integrity": "sha1-LasIutWjRDDdXMdRJx6sSpDq6Po=", 25 | "engines": { 26 | "node": ">=7" 27 | } 28 | } 29 | }, 30 | "dependencies": { 31 | "crypto-js": { 32 | "version": "4.1.1", 33 | "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", 34 | "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" 35 | }, 36 | "hex2ascii": { 37 | "version": "0.0.3", 38 | "resolved": "https://registry.npmjs.org/hex2ascii/-/hex2ascii-0.0.3.tgz", 39 | "integrity": "sha1-LasIutWjRDDdXMdRJx6sSpDq6Po=" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yt-blockchain", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "Carlos Azaustre (https://youtube.com/carlosazaustre)", 11 | "license": "MIT", 12 | "dependencies": { 13 | "crypto-js": "^4.1.1", 14 | "hex2ascii": "^0.0.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/block.js: -------------------------------------------------------------------------------- 1 | const SHA256 = require("crypto-js/sha256"); 2 | const hex2ascii = require("hex2ascii"); 3 | 4 | class Block { 5 | constructor(data) { 6 | this.hash = null; 7 | this.height = 0; 8 | this.body = Buffer.from(JSON.stringify(data).toString("hex")); 9 | this.time = 0; 10 | this.previousBlockHash = null; 11 | } 12 | 13 | validate() { 14 | const self = this; 15 | return new Promise((resolve, reject) => { 16 | let currentHash = self.hash; 17 | 18 | self.hash = SHA256(JSON.stringify({ ...self, hash: null })).toString(); 19 | 20 | if (currentHash !== self.hash) { 21 | return resolve(false); 22 | } 23 | 24 | resolve(true); 25 | }); 26 | } 27 | 28 | getBlockData() { 29 | const self = this; 30 | return new Promise((resolve, reject) => { 31 | let encodedData = self.body; 32 | let decodedData = hex2ascii(encodedData); 33 | let dataObject = JSON.parse(decodedData); 34 | 35 | if (dataObject === "Genesis Block") { 36 | reject(new Error("This is the Genesis Block")); 37 | } 38 | 39 | resolve(dataObject); 40 | }); 41 | } 42 | 43 | toString() { 44 | const { hash, height, body, time, previousBlockHash } = this; 45 | return `Block - 46 | hash: ${hash} 47 | height: ${height} 48 | body: ${body} 49 | time: ${time} 50 | previousBlockHash: ${previousBlockHash} 51 | -------------------------------------`; 52 | } 53 | } 54 | 55 | module.exports = Block; 56 | -------------------------------------------------------------------------------- /src/blockchain.js: -------------------------------------------------------------------------------- 1 | const SHA256 = require("crypto-js/sha256"); 2 | const Block = require("./block"); 3 | 4 | class Blockchain { 5 | constructor() { 6 | this.chain = []; 7 | this.height = -1; 8 | this.initializeChain(); 9 | } 10 | 11 | async initializeChain() { 12 | if (this.height === -1) { 13 | const block = new Block({ data: "Genesis Block" }); 14 | await this.addBlock(block); 15 | } 16 | } 17 | 18 | addBlock(block) { 19 | let self = this; 20 | return new Promise(async (resolve, reject) => { 21 | block.height = self.chain.length; 22 | block.time = new Date().getTime().toString(); 23 | 24 | if (self.chain.length > 0) { 25 | block.previousBlockHash = self.chain[self.chain.length - 1].hash; 26 | } 27 | 28 | let errors = await self.validateChain(); 29 | if (errors.length > 0) { 30 | reject(new Error("The chain is not valid: ", errors)); 31 | } 32 | 33 | block.hash = SHA256(JSON.stringify(block)).toString(); 34 | self.chain.push(block); 35 | resolve(block); 36 | }); 37 | } 38 | 39 | validateChain() { 40 | let self = this; 41 | const errors = []; 42 | 43 | return new Promise(async (resolve, reject) => { 44 | self.chain.map(async (block) => { 45 | try { 46 | let isValid = await block.validate(); 47 | if (!isValid) { 48 | errors.push(new Error(`The block ${block.height} is not valid`)); 49 | } 50 | } catch (err) { 51 | errors.push(err); 52 | } 53 | }); 54 | 55 | resolve(errors); 56 | }); 57 | } 58 | 59 | print() { 60 | let self = this; 61 | for (let block of self.chain) { 62 | console.log(block.toString()); 63 | } 64 | } 65 | } 66 | 67 | module.exports = Blockchain; 68 | --------------------------------------------------------------------------------