Implements:
- Basic in-memory blockchain
- Basic Proof of work
- Methods to serialize/deserialize complete chain
Again none of this is supposed to be production ready.
| const crypto = require('crypto'); | |
| let blockchain = []; | |
| class BlockchainError extends Error { | |
| constructor(message) { | |
| super(message); | |
| Error.captureStackTrace(this, this.constructor); | |
| } | |
| } | |
| function sha256(value) { | |
| return crypto.createHash('sha256', '').update(`${value}`).digest("hex") | |
| } | |
| function hash(parent_hash, timestamp, data) { | |
| return sha256(`${parent_hash}-${timestamp}-${JSON.stringify(data)}`); | |
| } | |
| function append({parent_hash, timestamp, data}) { | |
| if (!(parent_hash && timestamp && data)) { | |
| throw new Error('Invalid block'); | |
| } | |
| if (blockchain.length) { | |
| const lastBlock = blockchain[blockchain.length - 1]; | |
| if (lastBlock.hash !== parent_hash) { | |
| throw new BlockchainError(`Can not append child for ${parent_hash} after ${lastBlock.parent_hash}`) | |
| } | |
| } | |
| blockchain.push({ | |
| parent_hash, | |
| timestamp, | |
| data, | |
| hash: hash(parent_hash, timestamp, data) | |
| }); | |
| return true; | |
| } | |
| function proven(str) { | |
| return str.startsWith("51873"); | |
| } | |
| function proofOfWork(previous) { | |
| let next = previous + 1; | |
| let hash = sha256(next); | |
| while (!proven(hash)) { | |
| hash = sha256(++next); | |
| } | |
| return next; | |
| } | |
| function mine(payload) { | |
| while (true) { | |
| try { | |
| const lastBlock = blockchain[blockchain.length - 1]; | |
| const proof = proofOfWork(lastBlock.data.proof); | |
| const newBlock = { | |
| parent_hash: lastBlock.hash, | |
| timestamp: new Date().getTime(), | |
| data: { | |
| proof, | |
| payload, | |
| proof_digest: sha256(proof) | |
| } | |
| }; | |
| appended = append(newBlock); | |
| return newBlock; | |
| } catch (e) { | |
| if (e instanceof BlockchainError) { | |
| continue; | |
| } | |
| throw e; | |
| } | |
| } | |
| } | |
| function boot() { | |
| if (blockchain.length === 0) { | |
| append({ | |
| parent_hash: "~", | |
| timestamp: new Date().getTime(), | |
| data: { | |
| proof: 0, | |
| payload: [] | |
| } | |
| }); | |
| } | |
| } | |
| function serialize() { | |
| return JSON.stringify(blockchain); | |
| } | |
| function deserialize(val) { | |
| blockchain = JSON.parse(val); | |
| } | |
| function size() { | |
| return blockchain.length; | |
| } | |
| module.exports = { | |
| mine, | |
| boot, | |
| serialize, | |
| deserialize, | |
| size | |
| }; |
| const blockchain = require('./blockchain'); | |
| blockchain.boot(); | |
| while (true) { | |
| console.log( | |
| blockchain.mine([]) | |
| ); | |
| } |