了解区块链-用JS建造你自己的区块链
00-1010区块链太复杂了,说点简单的吧。用JS构建自己的区块链系统。只用几行代码就能解释区块链的底层数据结构、POW挖掘思想和事务处理流程等。当然,真实场景远比这复杂。这篇文章的目的只是让你对区块链有一个初步的了解。
文章主要参考了视频:用JavaScript构建区块链(https://www.youtube.com/playlist?list=plzvrqmj 9 hditqzmbtfisdxfxul 5k 0 f-Q4)
感谢原作者,本文在原视频的基础上进行了修改和补充,增加了个人理解。
00-1010区块链顾名思义就是由块组成的链,所以最基本的数据结构就是块。每个块都包含时间戳、数据、散列、先前散列和其他信息。其中data用于存储数据,previousHash是前一个块的哈希值。示意图如下:
哈希是块信息的汇总存储。hash的优点是任何长度的信息都可以通过hash映射成固定长度的字符串,比如sha256:
calculateHash() {
返回sha 256(this . previous hash this . timestamp JSON . stringify(this . data))。toString();
}
块的数据结构
块的最基本数据结构如下:
类块{
构造函数(时间戳,数据,先前哈希=\'\') {
this.timestamp=时间戳;
this.data=data
this . previous hash=previous hash;
hash的计算必须放在最后,计算前必须将所有数据赋值正确。
this . hash=this . calculate hash();
}
calculateHash() {
返回sha 256(this . previous hash this . timestamp JSON . stringify(this . data))。toString();
}
}
区块链的数据结构
区块链级
构造函数(){
this . chain=[];
}
}
基础块
所谓万事开头难,区块链的第一块总是需要人手工打造的。该块的previousHash为空,例如:
createGenesisBlock() {
返回新块(\' 2018-11-11 00:00:00 \',\'简单链的创世块\',\' \');
}
区块链的施工方法也应改为:
区块链级
构造函数(){
this . chain=[this . creategenesisblock()];
}
}
添加块
每增加一个新的块,它必须与原来的区块链连接,即:
区块链级
getLatestBlock() {
返回this . chain[this . chain . length-1];
}
addBlock(newBlock) {
//新块的前一个哈希值是现有区块链的最后一个块的哈希值;
new block . previous hash=this . getlatest block()。哈希;
//重新计算新块的哈希值(因为previous hash);是指定的);
new block . hash=new block . calculate hash();
//将新块添加到链中;
this . chain . push(new block);
}
.
}
检查区块链
区块链数据结构的核心是保证它是来回链接的,不能被篡改。但是如果真的有人篡改了一个区块,我们怎么去验证这个发现呢?最愚蠢和自然的想法是遍历所有情况并逐一检查,例如:
ischavalid(){
//遍历所有块
for(设I=1;I this . chain . length;i ) {
const current block=this . chain[I];
const previous block=this . chain[I-1];
//重新计算当前块的哈希值。如果哈希值不匹配,说明这个块中的数据被篡改了,哈希值没有重新计算。
if (currentBlock.hash!==current block . calculate hash()){
console.error(\'hash不等于: \' JSON . stringify(current block));
返回false
}
//判断当前块的previousHash是否真的等于前一块的。如果不是,则意味着前一个块已经被篡改。虽然哈希值重新计算正确,但后续块的哈希值没有重新计算,导致整个链断裂。
if (currentBlock.previousHash!==previous block . calculate hash){
console.error(\'以前的哈希不正确: \' JSON . stringify(current block));
返回false
}
}
返回true
}
运行它
跑上去一看,即:
设simpleChain=新区块链();
simpleChain.addBlock(新块(\' 2018-11-11 00:00:01 \',{ amount : 10 });
simpleChain.addBlock(新块(\' 2018-11-11 00:00:02 \',{金额: 20 });
控制台。log(JSON。stringify(简单链,null,4));
console.log(\'链有效吗?\'简单的链条。ischavalid());
结果如下:
阿里-186590 cc 4a 7f :单链山妖$ node main_1.js
{
链条\' : [
{
时间戳\' : \' 2018-11-11 00:00:00 \',
数据\":\"简单链的起源块,
以前哈希\' : \',
哈希: \' FD 56967 ff 621 a 4090 ff 71 ce 88 FDD 456547 D1 c 92d 2e 93766 B7 e 8791 f 7 a5 f 91 f 89 \'
},
{
时间戳\' : \' 2018-11-11 00:00:01 \',
数据\' : {
金额\' : 10
},
以前哈希: \' FD 56967 ff 621 a 4090 ff 71 ce 88 FDD 456547 D1 c 92d 2e 93766 b7e 8791 f 7 a5f 91f 89 \',
哈希: \' 150 b 196268 a 0152 e 9 f 0e 719 AC 131 a 722472 a 809 f 49 BD 507965029 a 78 c 7400529 \'
},
{
时间戳\' : \' 2018-11-11 00:00:02 \',
数据\' : {
金额\' : 20
},
以前哈希: \' 150 b 196268 a 0152 e 9 f 0e 719 AC 131 a 722472 a 809 f 49 BD 507965029 a 78 c 7400529 \',
哈希: \' 274 a7a 13 ed 20118 E8 CB 745654934 a7 e24 a4 d 59333 ba 17 DFB F5 d 4 cf E0 fa 8 a6 e 34 \'
}
]
}
链条有效吗?真实的
注意看其中的前一个哈希与哈希,确实是当前区块的前一个哈希指向前一个区块的哈希。
篡改下试试
都说区块链不可篡改,是真的吗?让我们篡改第2个区块试试,如:
设简单链=新区块链();
simpleChain.addBlock(新块(\' 2018-11-11 00:00:01 \',{金额: 10 });
simpleChain.addBlock(新块(\' 2018-11-11 00:00:02 \',{金额: 20 });
console.log(\'链有效吗?\'简单的链条。ischavalid());
//将第2个区块的数据,由10改为15
简单链.data={ amount : 15 };
console.log(\'链还有效吗?\'简单的链条。ischavalid());
控制台。log(JSON。stringify(简单链,null,4));
结果如下:
阿里-186590 cc 4a 7f :单链山妖$ node main_1.js
链条有效吗?真实的
哈希不等于:{ \'时间戳\' : \' 2018-11-11 003:00:01 \',\'数据\' : { \'金额\' :15},\'先前哈希D1 c 92d 2 e 93766 b 7 e 8791 f 7 a5 f 911
链条还有效吗?错误的
{
链条\' : [
{
时间戳\' : \' 2018-11-11 00:00:00 \',
数据\":\"简单链的起源块,
以前哈希\' : \',
哈希: \' FD 56967 ff 621 a 4090 ff 71 ce 88 FDD 456547 D1 c 92d 2e 93766 B7 e 8791 f 7 a5 f 91 f 89 \'
},
{
时间戳\' : \' 2018-11-11 00:00:01 \',
数据\' : {
金额\' : 15
},
以前哈希: \' FD 56967 ff 621 a 4090 ff 71 ce 88 FDD 456547 D1 c 92d 2e 93766 b7e 8791 f 7 a5f 91f 89 \',
哈希: \' 150 b 196268 a 0152 e 9 f 0e 719 AC 131 a 722472 a 809 f 49 BD 507965029 a 78 c 7400529 \'
},
{
时间戳\' : \' 2018-11-11 00:00:02 \',
数据\' : {
金额\' : 20
},
以前哈希: \' 150 b 196268 a 0152 e 9 f 0e 719 AC 131 a 722472 a 809 f 49 BD 507965029 a 78 c 7400529 \',
哈希: \' 274 a7a 13 ed 20118 E8 CB 745654934 a7 e24 a4 d 59333 ba 17 DFB F5 d 4 cf E0 fa 8 a6 e 34 \'
}
]
}
显然,篡改了数据之后,哈希值并未重新计算,导致该区块的混杂值对不上。
再篡改下试试
那么,如果我们聪明点,篡改后把混杂值也重新计算会如何?
设简单链=新区块链();
simpleChain.addBlock(新块(\' 2018-11-11 00:00:01 \',{金额: 10 });
simpleChain.addBlock(新块(\' 2018-11-11 00:00:02 \',{金额: 20 });
console.log(\'链有效吗?\'简单的链条。ischavalid());
//篡改后重新计算混杂值
简单链.data={ amount : 15 };
简单链.hash=simpleChain.chain[1].计算hash();
console.log(\'链还有效吗?\'简单的链条。ischavalid());
控制台。log(JSON。stringify(简单链,null,4));
结果如下:
显然,第3个区块的前一个哈希并未指向第2个区块的哈希。
是真的无法篡改吗
其实并不是,如果我们再聪明一点,把后续区块的混杂值也重新计算一下,不就好了吗?确实如此,如:
设简单链=新区块链();
simpleChain.addBlock(新块(\' 2018-11-11 00:00:01 \',{金额: 10 });
simpleChain.addBlock(新块(\' 2018-11-11 00:00:02 \',{金额: 20 });
console.log(\'链有效吗?\'简单的链条。ischavalid());
//篡改第2个区块
简单链.data={ amount : 15 };
简单链.hash=simpleChain.chain[1].计算hash();
//并把第3个区块也重新计算
简单链.以前的哈希=简单链。链条,链条.哈希;
简单链.hash=simpleChain.chain[2].计算hash();
console.log(\'链还有效吗?\'简单的链条。ischavalid());
控制台。log(JSON。stringify(简单链,空,4
作者:扁鹊他大哥