新区块校验
当挖矿挖出来了新的区块,我们需要把这个区块进行校验,既然要校验这个区块,那么就需要先把这个区块的结构给拼接出来,因此,在上一个小节的基础上,我们先把生成区块的逻辑先抽离出来
index.js
const crypto = require('crypto')
class BlockChain {
constructor() {
// 定义一个创世区块,即第一个区块的数据结构
this.firstBlock = {
index: 0,
data: 'hello world',
prevHash: '0',
timestamp: 1679234322447,
hash: 'f0c40a6c9ea4b9ae794c89744b45a2aa10af6d6d561046d039db981f94a5073b'
}
// 区块链数组
this.blockchain = [this.firstBlock]
// 难度值
this.difficulty = 4
}
// 生成区块的hash值
createHash({ index, prevHash, timestap, data, nonce }) {
return crypto
.createHash('sha256')
.update(index + prevHash + timestap + data + nonce)
.digest('hex')
}
// 挖矿
mine() {
// 这里需要对新区块进行校验
let newBlock = this.createNewBlock()
// 接下来的逻辑就是校验这个新区块,然后加入到区块链中
}
// 创建新区块的逻辑
createNewBlock() {
// 记录挖矿次数
let nonce = 0
// 记录当前是第几个区块
let index = this.blockchain.length
// 交易数据
let data = 'A -> B: 100'
// 最后一个区块
let prevHash = this.blockchain[this.blockchain.length - 1].hash
let timestamp = new Date().getTime()
let hash = this.createHash({ index, prevHash, timestamp, data, nonce })
while (hash.slice(0, this.difficulty) !== '0'.repeat(this.difficulty)) {
nonce++
hash = this.createHash({ index, prevHash, timestamp, data, nonce })
}
return {
index,
data,
prevHash,
timestamp,
hash,
nonce
}
}
}
let b = new BlockChain()
b.mine()
先分析校验新区块的思路,我们需要校验哪些点
1.生成的新区块的时间戳,是否是大于区块链中最后的那个区块
2.生成的新区块的prevHash,是否等于区块链中最后的那个区块的hash
3.生成的新区块的难度值,是否符合我们设定的难度值
4.生成的新区块的index,是否等于区块链中最后的那个区块的index + 1
5.生成的新区块的随机值,是否被篡改
注意:以上校验点1、2、4都是和区块链中最后一个区块进行对比验证
接下来,我们就去具体实现代码, 我们单独写一个方法来验证
validBlock(newBlock, lastBlock) {
// 校验判断
if (
newBlock.timestamp < lastBlock.timestamp ||
newBlock.index !== lastBlock.index + 1 ||
newBlock.prevHash !== lastBlock.hash ||
newBlock.hash.slice(0, this.difficulty) !== "0".repeat(this.difficulty) ||
newBlock.hash !== this.createHash(newBlock)
) {
return false;
}
return true;
}
新区块校验完了以后,我们还需要校验整个区块链是否被篡改过
接下来,还需要封装一个函数,对整个区块链和新区块进行校验
validBlockChain(blockchain) {
// 从后往前验证
for (let i = blockchain.length - 1; i >= 1; i--) {
if (!this.validBlock(blockchain[i], blockchain[i - 1])) {
return false;
}
}
// 校验创世区块
if (JSON.stringify(blockchain[0]) !== JSON.stringify(this.firstBlock)) {
return false;
}
return true;
}
最后,在挖矿函数中,对新出的区块和整个区块链进行校验
// 挖矿
mine() {
// 挖出新区块
let newBlock = this.createNewBlock();
// 获取到区块链中最后的区块
let lastBlock = this.getLastBlock();
// 校验区块和区块链的合法性
if (
this.validBlock(newBlock, lastBlock) &&
this.validBlockChain(this.blockchain)
) {
this.blockchain.push(newBlock);
} else {
console.log("错误:区块链被篡改 ", this.blockchain);
}
}
上面函数中,我们封装了一个方法来获取区块链中最后一个区块
getLastBlock() {
return this.blockchain[this.blockchain.length - 1];
}
接下来,测试效果
let b = new BlockChain()
b.mine()
b.blockchain[1].nonce = 99
b.mine()
console.log(b.blockchain)
结果如下
结果符合预期
到目前为止,index.js 里面的完整代码如下,如果你写代码的过程中有报错,查看下面完整的代码,看看是否有遗漏
const crypto = require('crypto')
class BlockChain {
constructor() {
// 定义一个创世区块,即第一个区块的数据结构
this.firstBlock = {
index: 0,
data: 'hello world',
prevHash: '0',
timestamp: 1679234322447,
hash: 'f0c40a6c9ea4b9ae794c89744b45a2aa10af6d6d561046d039db981f94a5073b'
}
// 区块链数组
this.blockchain = [this.firstBlock]
// 难度值
this.difficulty = 4
}
// 生成区块的hash值
createHash({ index, prevHash, timestap, data, nonce }) {
return crypto
.createHash('sha256')
.update(index + prevHash + timestap + data + nonce)
.digest('hex')
}
// 挖矿
mine() {
// 挖出新区块
let newBlock = this.createNewBlock()
// 获取到区块链中最后的区块
let lastBlock = this.getLastBlock()
// 校验区块和区块链的合法性
if (
this.validBlock(newBlock, lastBlock) &&
this.validBlockChain(this.blockchain)
) {
this.blockchain.push(newBlock)
} else {
console.log('错误:区块链被篡改 ', this.blockchain)
}
}
createNewBlock() {
// 记录挖矿次数
let nonce = 0
// 记录当前是第几个区块
let index = this.blockchain.length
// 交易数据
let data = 'A -> B: 100'
// 最后一个区块
let prevHash = this.blockchain[this.blockchain.length - 1].hash
let timestamp = new Date().getTime()
let hash = this.createHash({ index, prevHash, timestamp, data, nonce })
while (hash.slice(0, this.difficulty) !== '0'.repeat(this.difficulty)) {
nonce++
hash = this.createHash({ index, prevHash, timestamp, data, nonce })
}
return {
index,
data,
prevHash,
timestamp,
hash,
nonce
}
}
validBlock(newBlock, lastBlock) {
// 校验判断
if (
newBlock.timestamp < lastBlock.timestamp ||
newBlock.index !== lastBlock.index + 1 ||
newBlock.prevHash !== lastBlock.hash ||
newBlock.hash.slice(0, this.difficulty) !== '0'.repeat(this.difficulty) ||
newBlock.hash !== this.createHash(newBlock)
) {
return false
}
return true
}
validBlockChain(blockchain) {
// 从后往前验证
for (let i = blockchain.length - 1; i >= 1; i--) {
if (!this.validBlock(blockchain[i], blockchain[i - 1])) {
return false
}
}
// 校验创世区块
if (JSON.stringify(blockchain[0]) !== JSON.stringify(this.firstBlock)) {
return false
}
return true
}
getLastBlock() {
return this.blockchain[this.blockchain.length - 1]
}
}
let b = new BlockChain()
b.mine()
b.blockchain[1].nonce = 99
b.mine()
console.log(b.blockchain)