命令行工具使用
这个小节我们需要给整个 node-chain 项目添加上命令行工具,就像使用 vue-cli 一样运行我们的项目相关功能
自定义命令流程
开发命令行工具我们这里使用一个第三方包,叫做 commander,这个包可以解析命令行参数并且执行相应的操作,下面我们先让 commander 跑起来
第 1 步,安装 commander 包
npm install commander
第 2 步,调整我们当前项目的结构,新建一个 blockchain.js 文件,把 index.js 的代码剪切过去,项目结构如下
第 3 步,在 index.js 编写如下代码
#!/usr/bin/env node
console.log('自定义命令执行了')
第 4 步,在 package.json 加入下面配置代码
"bin":{
"node-chain": "./index.js"
},
第 5 步,把 node-chain 这个命令链接到全局,你需要在项目目录下执行命令
npm link
第 6 步,测试效果
node-chain
在命令行输入命令 node-chain,我们在 index.js 里面的代码被执行了
结果符合预期
commander 使用
index.js 中添加如下代码
#!/usr/bin/env node
const { Command } = require('commander')
const program = new Command()
program
.name('node-chain')
.description('这是一个区块链演示工具')
.version('0.0.1')
program
.command('mine ')
.description('挖矿')
.action(() => {
console.log('执行挖矿')
})
program.parse()
我们再次运行 node-chain 命令,执行结果如下
区块链加入命令行
第 1 步,修改 blockchain.js 代码,这里需要修改两处
第 1 处代码是,在执行完挖矿函数后,我们把得到的新区块返回出去,这样我们能在命令行代码里面使用到
// 挖矿
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);
return ''
}
// 这里返回新区块
return newBlock;
}
第二处需要修改的是,把整个 BlockChain 类导出
module.exports = Blockchain
修改后的代码如下:
blockchain.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)
}
return newBlock
}
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]
}
}
module.exports = BlockChain
第 2 步,在 index.js 文件中,添加如下代码
index.js
#!/usr/bin/env node
const { Command } = require('commander')
const program = new Command()
// 引入区块链类
const BlockChain = require('./blockchain')
const blockchain = new BlockChain()
program
.name('node-chain')
.description('这是一个区块链演示工具')
.version('0.0.1')
program
.command('mine ')
.description('挖矿')
.action(() => {
const newBlock = blockchain.mine()
if (newBlock) {
console.log(newBlock)
}
})
program.parse()
第 3 步,执行测试
node-chain mine
执行结果
结果符合预期
最后,我们按照上面的代码逻辑,再添加一个查看区块链的命令
#!/usr/bin/env node
const { Command } = require('commander')
const program = new Command()
// 引入区块链类
const BlockChain = require('./blockchain')
const blockchain = new BlockChain()
program
.name('node-chain')
.description('这是一个区块链演示工具')
.version('0.0.1')
program
.command('mine ')
.description('挖矿')
.action(() => {
const newBlock = blockchain.mine()
if (newBlock) {
console.log(newBlock)
}
})
program
.command('view')
.description('查看完整区块链信息')
.action(() => {
console.log(blockchain.blockchain)
})
program.parse()
我们运行命令查看结果
node-chain view
结果如下
命令已经跑通,但是结果还有问题,每次挖矿过后,区块链并没有做持久化操作,因此,当你查看的时候,区块链中只有一个区块,这个问题,我们下个小节来解决