D~DIDI~DIDIDI!!!!

0%

区块链学习一:区块链

最近在刷CTF web题目的时候,涉及到了区块链相关的题目,然而自己对区块链的认识也仅限于BTC是和区块链有关的,于是赶紧找了点大佬的文章开始阅读并学习,也在大佬的基础上自己重新记录了一下笔记,按照自己的一些理解记录下来,先贴出大佬的链接,我的这个博客,也将和大佬的文章一样,分成四篇来写(其实就是太菜,只能跟着大佬的思路走)

大佬的链接

从零开始构建一个区块链 (一):区块链

从零开始构建一个区块链 (二):工作量证明

从零开始构建一个区块链 (三):API

从零开始构建一个区块链 (四):共识

正文

一、区块(Block)

区块是构建区块链的基本单位。一个区块至少要包含以下信息:

  • Index:区块在区块链中的位置;
  • timestamp:区块产生的时间;
  • transaction:区块包含得交易;
  • previousHash:前一个区块得Hash值;
  • Hash:当前区块的Hash值;

区块链不可篡改特征有最后两个属性保证,previousHash和Hash是区块链的关键

根据这些,开始构造一个Block类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const SHA = reqire('crypto-js/sha256');
class Block{
constructor(index, timestap){
this.index = index;
this.timestap = timestap;
this.transactions = [];
this.previousHash = '';
this.Hash = '';
}
calculateHash(){
return SHA256(this.index + this.previousHash + this.timestap + JSON.stringify(this.transactions)).tostring();
}

addNewTransaction(sender, recipient, amount){
this.transactions.push({
sender,
recipient,
amount
})
}
getTransactions(){
return this.transactions;
}

在这串js代码中,使用了crypto-js里的SHA256来作为区块的Hash算法,这也是比特币的中使用的算法,transactions为一系列交易对象的列表,每笔交易的格式为:

1
2
3
4
5
{
sender: sender,
recipient: recipient,
amount: amount
}

calculateHash方法根据当前区块的信息计算出Hash的值

二、区块链(Blockchain)

区块链构建好后,就需要把多个区块组成一个区块链。

一个区块链就是一个列表,列表里的每个元素就是一个区块。

区块链需要一个创世区块(Genesis Block),这是需要手工生成的区块链的第一个区块

生成区块链的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Blockchain{

constructor(){
this.chain = [this.createGenesisBlock()];
}

createGenesisBlock(){//创建创世块
const genesisBlock = new Block(0, "01/11/2019");
genesisBlock.previousHash = '0';
genesisBlock.addNewTransaction('xianyu','five',520);
return genesisBlock;
}

getLastestBlock(){
return this.chain[this.chain.length - 1];
}

addBlock(newBlock){
newBlock.previousHash = this.getLastestBlock().hash;
newBlock.hash = newBlock.calculateHash();
this.chain.push(newBlock);
}

isChainValid(){
for(let i = 1; i < this.chain.length; i++){
const currentBlock = this.chain[i];
const previousBlock = this.chain[i - 1];
if(currentBlock.hash !== currentBlock.calculateHash()){
return false;
}
if(currentBlock.previousHash !== previousBlock.hash){
return false;
}
}
return true;
}
}

在Blockchain这个类中,我们实现了一个创建创世块的方法,因为创世块是没有前面的区块的,所以创世块的previousHash设置为0,我这里给得值事xianyu给five发了520块钱,这样就产生了一笔交易并记录到了创世块之中,之后再把创世块添加到构造函数中,这样区块链中就包含一个创世区块了,getLastBlock和addBlock分别是取得最后一个区块链和添加一个区块链。isChainValid方法是用来检测区块链是否有效的。如果已经添加到区块链的区块发生了变化(被篡改),那么这个方法就会返回false

三、测试区块链

前面的内容已经完成了一个区块链,现在我们需要想区块链中添加两个完整的区块。并尝试修改区块信息来展示区块链的不可篡改特性。

先创建一个名字叫做xianyuCoin的区块链,使用Blockchain类建立一个新的对象,此时它应该只包含创世区块。

1
2
const xianyuCoin= new Blockchain();
console.log(JSON.stringify(xianyuCoin.chain, undefined, 2));

使用node.js运行这个js文件,可以得到如下数据

1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{

"index": 0,

"timestamp": "01/11/2019",

"transactions": [

{

"sender": "xianyu",

"recipient": "five",

"amount": 520

}

],

"previousHash": "0",

"hash": "43425ab92a78af0d03a6d869cb5dc9757feb7f5c0ae85e25e476617a4a94eab0"

}

接着,再新建两个新的区块,依次添加到这个区块链中

1
2
3
4
5
6
7
8
9
block1 = new Block('1', '02/11/2019');
block1.addNewTransaction('didi','haha',100);
xianyuCoin.addBlock(block1);

block2 = new Block('2','03/11/2019');
block2.addNewTransaction('QQ', 'TIM', 10000);
xianyuCoin.addBlock(block2);

console.log(JSON.stringify(testCoin.chain, undefined, 2));//log输出json数据

运行js,得到一下JSON数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
{
"index": 0,
"timestamp": "01/11/2019",
"transactions": [
{
"sender": "xianyu",
"recipient": "five",
"amount": 520
}
],
"previousHash": "0",
"hash": "43425ab92a78af0d03a6d869cb5dc9757feb7f5c0ae85e25e476617a4a94eab0"
},

{
"index": "1",
"timestamp": "02/11/2019",
"transactions": [
{
"sender": "didi",
"recipient": "haha",
"amount": 100
}
],
"previousHash": "43425ab92a78af0d03a6d869cb5dc9757feb7f5c0ae85e25e476617a4a94eab0",
"hash": "4b0c3391a9a40563b10238dc35983b2c3c9b20460d4bba9da7084ef2c40844a0"
},
{
"index": "2",
"timestamp": "03/11/2019",
"transactions": [
{
"sender": "QQ",
"recipient": "TIM",
"amount": 10000
}
],
"previousHash": "4b0c3391a9a40563b10238dc35983b2c3c9b20460d4bba9da7084ef2c40844a0",
"hash": "4013ade9b04e1fc8c70f63866e1f7555ab56f71828d287e5bda52288e759c9fd"
}

现在我们的xianyuCoin已经拥有了3个区块,且第二和第三个区块的previousHash值都是上一个区块的Hash值,第一个区块为创世区块所以它的previousHash的值为0;

这个时候我们可以使用前面写好的isChainValid()方法去检验区块链的有效性

加一行:console.log(xianyuCoin.isChainValid());

2

执行程序验证成功

3

接下来,检测区块链的防篡改特性

我们对区块链的第一个区块的交易信息进行修改,这里篡改为didi只给了haha一元钱

1
2
block1.transactions[0].amount = 1;
console.log(block1.getTransactions());

4

这里已经将信息修改成功

5

我们在对区块链进行检测

6

可以看到检测失败,证明区块链已被篡改

7

我们从这个区块链的实现方式来分析,最开始didi给了haha100块,计算并赋值给了Hash,当我们把100块修改为1块的时候,通过calculateHash方法计算出的Hash值已经发生了变化,和区块中Hash的值不同,于是返回false,区块已被篡改,也就是这段代码的作用:

8

这时候,又考虑再修改amount时同时将区块的Hash值也一起修改,并执行检测方法

9

程序再次返回false

10

这时候,每一个区块都存储了上一个区块的Hash值,也就是previousHash,当我们将100块改为1块并将当前区块的的Hash值也进行修改的时候,当前区块的Hash值便发生了变化,但是它后面一个区块,也就是block2所存储的previousHash的值并没有发生变化和当前块的Hash值进行比较,两者不同,便返回false,区块已被篡改,也就是这段代码的作用

11

第一篇就到这里

源码地址

2019052010