最近在刷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 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));
|
运行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());
执行程序验证成功
接下来,检测区块链的防篡改特性
我们对区块链的第一个区块的交易信息进行修改,这里篡改为didi只给了haha一元钱
1 2
| block1.transactions[0].amount = 1; console.log(block1.getTransactions());
|
这里已经将信息修改成功
我们在对区块链进行检测
可以看到检测失败,证明区块链已被篡改
我们从这个区块链的实现方式来分析,最开始didi给了haha100块,计算并赋值给了Hash,当我们把100块修改为1块的时候,通过calculateHash方法计算出的Hash值已经发生了变化,和区块中Hash的值不同,于是返回false,区块已被篡改,也就是这段代码的作用:
这时候,又考虑再修改amount时同时将区块的Hash值也一起修改,并执行检测方法
程序再次返回false
这时候,每一个区块都存储了上一个区块的Hash值,也就是previousHash,当我们将100块改为1块并将当前区块的的Hash值也进行修改的时候,当前区块的Hash值便发生了变化,但是它后面一个区块,也就是block2所存储的previousHash的值并没有发生变化和当前块的Hash值进行比较,两者不同,便返回false,区块已被篡改,也就是这段代码的作用
第一篇就到这里
源码地址