引见了一些关于比特币的观点与机制,为了加深明白,本文基于JavaScript来完成一个简朴的区块链原型,后续再对其举行不断雄厚。
1. 概述
如前所述区块链模子的组成部分,包括区块,区块组成的区块链,以及保留区块链的数据耐久层等。一个超简朴的UML类图以下:
因为我是前端的,业余看了这么久区块链的理论,照样手痒痒感谢代码,把这个类用JavaScript完成一下。写完以后发明现在阶段,关于区块链原型来讲照样太甚简朴,不过如果说用来做前端面试题,考核下面向对象和Promise等知识点却是挺联系。
2. 定义区块数据模子
摘取比特币区块的概况举行修正,去除一切过剩信息,只留下能形貌区块最基本的信息,声明区块类以下:
class Block {
constructor(data) {
// 区块的属性值
this.hash = "";
this.height = 0;
this.body = data;
this.time = 0;
this.previousBlockHash = "";
}
}
module.exports.Block = Block;
3. 数据耐久层
其有用数组完成区块链是最简朴的原型计划,但每次重启数组都会被清空,数据并不耐久。所以这里引入levelDB
数据库作为耐久层来保留数据,相干操纵可参考level。因为直接挪用API,关于应用层来讲过于贫苦,所以在此声明一个数据操纵类LevelSandbox
,该类不像传统的关联型数据库具有增、删、改、查等悉数功用,因为区块链上数据的不可更改性,此类只包括增和查的操纵。
3.1 依据key从数据库中猎取数据
本文以下相干异步完成,都采纳Promise的体式格局而非回调,个中优点作为前端工程师此处就不多引见了,有须要相识的可异步Promise引见,自行扩大浏览。
getLevelDBData(key) {
let self = this;
return new Promise(function(resolve, reject) {
self.db.get(key)
.then(value => {
console.log('Value = ' + value);
resolve(value)
})
.catch(err => {
console.log('Not found!');
reject(err)
})
});
}
3.2 将key/value
数据插进去数据库中
以key/value
的体式格局在数据库中存储,其key
值得拔取,这里斟酌运用区块类中声明的height
字段,该字段标识一个区块在链中的位序,同时也具有唯一性,异常适宜。
addLevelDBData(key, value) {
let self = this;
return new Promise(function(resolve, reject) {
self.db.put(key, value)
.then(() => resolve())
.catch((err) => {
console.log('Block ' + key + ' submission failed');
reject(err)
})
});
}
3.3 猎取数据库中区块总数
createReadStream()
要领建立一个读取数据库的流,这里的作用是为了遍历整库以猎取存储的区块总数,别的此要领还可经由历程传参,设置遍历序次,概况可参阅文档。
getBlocksCount() {
let self = this;
return new Promise(function(resolve, reject){
let height = 0;
self.db.createReadStream()
.on('data', function () {
height++;
})
.on('error', function (error) {
reject('Unable to read data stream!', error);
})
.on('close', function () {
resolve(height);
});
});
}
4. 区块链类
该类重要担任将新建立的区块添加进区块链,并考证链中各个区块的数据完整性。这个历程当中少不了对区块数据的哈希处置惩罚,为轻易起见,采纳第三方库crypto-js完成的SHA256
要领。
设想该类中的重要要领包括:
- createGenesisBlock():天生肇端区块
- getBlockHeight():猎取区块链长度
- getBlock(height):猎取指定区块
- addBlock(block):将一个新区块到场区块链中
- validateBlock(block):考证某个区块
- validateChain():考证区块链
以下便完成个中重要的几个要领:
4.1 增添新区块
各个区块经由历程previousBlockHash
属性,顺次指向前一个区块来连接成链的,除首区块该属性为空外。
addBlock(block) {
return this.getBlockHeight()
.then(height => {
区块高度
block.height = height;
// UTC 时候戳
block.time = new Date().getTime().toString().slice(0, -3);
if (height > 0) {
this.getBlock(height - 1)
.then(preBlock => {
// 前一个区块的哈希值
block.previousBlockHash = preBlock.hash;
// 对区块举行哈希处置惩罚
block.hash = SHA256(JSON.stringify(block)).toString();
// 将新区快存入库中
this.bd.addLevelDBData(height, JSON.stringify(block));
})
.catch(error => console.log(error));
} else {
block.hash = SHA256(JSON.stringify(block)).toString();
this.bd.addLevelDBData(height, JSON.stringify(block));
}
})
.catch( error => console.log(error));
}
4.2 考证单个区块完整性
考证要领就是应用了hash算法的性子:雷同的数据经由hash后会天生雷同的hash值。
validateBlock(height) {
// 猎取区块的值
return this.getBlock(height)
.then(block => {
const objBlock = JSON.parse(block);
let blockHash = objBlock.hash;
objBlock.hash = '';
// 从新天生区块的哈希值
let validBlockHash = SHA256(JSON.stringify(objBlock)).toString();
objBlock.hash = blockHash;
// 比较以考证完整性
if (blockHash === validBlockHash) {
return Promise.resolve({isValidBlock: true, block: objBlock});
} else {
console.log('Block #'+blockHeight+' invalid hash:\n'+blockHash+'<>'+validBlockHash);
return Promise.resolve({isValidBlock: false, block: objBlock});
}
})
}
4.3 考证全部区块链
经由历程顺次校验每一个区块以考证整条链的完整性。
validateChain() {
let errorLog = [];
let previousHash = '';
this.getBlockHeight()
.then(height => {
for (let i = 0; i < height; i++) {
this.getBlock(i)
.then(block => this.validateBlock(block.height))
.then(({isValidBlock, block}) => {
if (!isValidBlock) errorLog.push(i);
if (block.previousBlockHash !== previousHash) errorLog.push(i);
previousHash = block.hash;
if (i === height - 1) {
if (errorLog.length > 0) {
console.log(`Block errors = ${errorLog.length}`)
console.log(`Blocks: ${errorLog}`)
} else {
console.log('No errors detected')
}
}
})
}
})
}
5. 天生测试数据
(function theLoop (i) {
setTimeout(function () {
let blockTest = new Block.Block("Test Block - " + (i + 1));
myBlockChain.addBlock(blockTest).then((result) => {
console.log(result);
i++;
if (i < 10) theLoop(i);
});
}, 10000);
})(0);
作为一个区块链原型的模样算是初见端倪,但就现在的功用来讲还异常大略,说是原型都算抬举了,不过背面逐步再雄厚吧。这里也只算是对之前的一个实践性的小节。
文中以列出重要代码片断,团体完成实在不难,没贴出一切代码重要是为了表述思绪更清楚些,如有朋侪完成历程当中有题目,可文下留言交换。