最简区块链Demo | 基于NodeJS实现区块链公有链
前言
注:文末有 源码地址
"凡是可以用 JavaScript 来写的应用,最终都会用 JavaScript 来写。" ——Atwood定律
【本文重心】:
如何以最简代码打造一款区块链
,
基于NodeJS如何开发一款区块链公有链
本文将以
解读源码
的形式,让你有如下收获:
即: 每隔2秒,一个新的
区块
被挖出,并缀连到前一个区块的尾部,如此形成
区块链
,同时打印出来。
constructor(height, previousHash, timestamp, data, hash) {
this.height = height
this.previousHash = previousHash + ''
this.timestamp = timestamp
this.data = data
this.hash = hash + ''
//根据上一个区块生成下一个区块
static generateBlock(blockData, previousBlock) {
const nextHeight = previousBlock.height + 1;
const nextTimeStamp = new Date().getTime();
const nextHash = CryptoJS.SHA256(nextHeight + previousBlock.hash + nextTimeStamp + blockData) + '';
return new Block(nextHeight, previousBlock.hash, nextTimeStamp, blockData, nextHash);
如上所见,区块的数据结构主要由以下几个字段:
height
: 当前这个区块的高度(所谓的高度,就是这个区块被挖出来的顺序,第一个,第二个...)
previousHash
: 前一个区块的
hash
(你可以简单认为
hash
就是一个类似身份证号的唯一识别码)
timestamp
: 即当前时间戳(用来指明当前高度的区块被挖出的时间)
data
: 区块除了上述
区块头
中的字段之外,在
区块体
中需要存放这一小段时间内该链上的
事件
/
交易
,这些
事件
/
交易
放在data字段中,当该区块被添加到区块链上之后,这些事件、交易就跟着
区块
被写入了
区块链
,从此具有了
可追溯
,
不可篡改
等特性
hash
:即当前
区块
将所有的
区块头
和
区块体
的数据打包后,计算出的一个唯一识别码。
三. 上述生成的区块如何构成区块链的?
其实根据上述
区块间缀连
的关系,已经知道了区块通过
hash
前后相连,逐步形成了
区块链
。
下面通过源码解读,区块间如何通过hash相连,又有哪些需要注意的地方?
这就是一个区块链
最简最朴实无华且枯燥
的样子:
class BlockChain {
constructor() {
this.blocks = [];
现在我希望将一个有一个的区块逐步缀连到前一个区块上,即用previousHash字段指明前一区块是谁!那么问题来了,第一个区块的previousHash是什么?也就是第一个区块该跟在谁后面?此刻什么都没有,难道跟着上帝么?
没错!就是跟着上帝!即区块链中的上帝,也就是固化在源码中,所谓的创世区块:
getGenesisBlock() {
return new Block(0, '0', 1595490064640, 'GenesisBlock', '0000000000000000000d87bedef9550a014af9a3af74b791d84d049cc3ca85f4')
如上所见,创世区块做了这么几件事:
height: 将高度设定为0
previousHash: 将创世区块的前一hash置为0
timestamp: 取当前时间戳:1595490064640(即2020-08-03 18:00))
data: 区块的数据体中,目前就是朴实无华的一句话,你写什么都OK!
hash: 根据上述字段计算出来的一个唯一的识别码
现在有了创世区块了,那么接下来的第一个区块怎么生成呢?源码在此:
generateNextBlock(blockData) {
const previousBlock = this.getLatestBlock()
const nextIndex = previousBlock.height + 1
const nextTimeStamp = new Date().getTime()
const nextHash = this.calcuteHash(nextIndex, previousBlock.hash, nextTimeStamp, blockData)
return new Block(nextIndex, previousBlock.hash, nextTimeStamp, blockData, nextHash)
如上可见,后继区块使用到了前一区块的高度height和哈希值hash。
当区块链发现有一个新的区块被挖出,且需要添加到该链的尾部时,必须得验证该块是否合乎要求:
isValidNewBlock(newBlock, previousBlock) {
!(newBlock instanceof Block) ||
!(previousBlock instanceof Block)
return false
// 判断height
if(newBlock.height !== previousBlock.height + 1) {
return false
// 判断hash值
if(newBlock.previousHash !== previousBlock.hash) {
return false
// 计算新块的hash值是否符合规则
if(this.calcuteHash(newBlock.height, newBlock.previousHash, newBlock.timestamp, newBlock.data) !== newBlock.hash) {
return false
return true
由上可见,判断一个新区块能否被添加到该区块链的依据有如下四点:
当前挖出的新区块newBlock和取出的上一区块previousBlock是否是合法的区块?
newBlock的高度,是否比previousBlock的高度刚好大1?
newblock的前一哈希: previousHash,是否刚好是previousBlock哈希: hash?
newBlock的hash是否是根据该区块的所有信息按固定规范加密生成的?
只有当上述几点全部满足,该新区块newBlock才能被加入该区块链,否则该区块将成为垃圾区块,游离于三界之外!
在判定区块合法之后,现在将其加入区块链:
addBlock(newBlock) {
if(this.isValidNewBlock(newBlock, this.getLatestBlock())) {
this.blocks.push(newBlock)
return true
return false
就这样,第一个区块连到创世区块后面,第二个区块连到第一个区块后面,如此重复,由一个个区块构成的区块链就诞生了。
你可以发现,每个区块只能被添加到区块链的后面,而不能将某个区块从区块链中移除!
同时,每个区块的区块体中,写有一些事件/交易,随着区块被固定在区块链中,该区块中的数据也不会被更改。这就是区块链最典型的特性: 不可篡改。
引申(此段可暂时不看,不影响全文逻辑)
然而在实际过程中,有时会发生多个区块相连而成的区块链片段需要被添加到区块链,在此,请问:
如何校验该片段是否合法?
如何将其加入区块链?
答案如下,详见源码:
* 判断新插入的区块链是否合法而且可以覆盖原来的节点
* @param {Array} newChain
isValidNewChain(newChain) {
if(Array.isArray(newChain) === false || newChain.length === 0) {
return false
let newChainLength = newChain.length,
firstBlock = newChain[0]
// 硬编码的起源块不能改变
if(firstBlock.height === 0) {
return false
// 移植新的链的长度 <= 现有链的长度
// 新的链不可信
if(newChainLength + firstBlock.height <= this.blocks.length) {
return false
// 下面检查新的链能否移植
// 以及新的链的每个节点是否符合规则
if(!this.isValidNewBlock(firstBlock, this.blocks[firstBlock.height - 1])) {
return false
for(let i = 1; i < newChainLength; ++i) {
if(!this.isValidNewBlock(newChain[i], newChain[i - 1])) {
return false
return true
* 插入新链表
* @param {Array} newChain
addChain(newChain) {
if(this.isValidNewChain(newChain)) {
const height = newChain[0].height
this.blocks.splice(height)
this.blocks = this.blocks.concat(newChain)
return true
return false
获取完整源码: Github
快速上手/如何运行
下载源码: git clone https://github.com/stevekeol/YunDang-Chain
切换至对应目录,并安装依赖项: cd yundang-chain/demo && npm install
运行: npm start
本文仅以近乎于Demo的形式,最简的展示了:
何为代码层面的区块链
如何以最简代码打造区块链
近期已提上日程的计划是打造一款 基于NodeJS的全节点公有链: YunDang-Chain。
我们将带你手把手实现它,参考 项目源码ReadMe,其中有个人微信和进群方式。