如何在 Node.js 中构建简单的加密货币区块链
已发表: 2022-03-10smashingCoin
币。 试一试——它比你想象的要简单!加密货币的空前兴起及其支撑的区块链技术已经席卷了世界——从十多年前作为一个学术概念的卑微开端到目前在各个行业中越来越多的采用。
区块链技术因其增强去信任环境中的安全性、强制去中心化和提高流程效率的能力而受到广泛关注。
传统上,Python 一直是区块链开发的事实上的编程语言。 然而,随着这项令人惊叹的技术的普及,开发选项也增加了——Node.js 并没有落伍。
在本教程中,我将讨论如何在 Node.js 中构建一个简单的加密货币区块链。 它不会太花哨,但足以帮助您了解区块链的工作原理。
我将把这个简单的加密货币称为smashingCoin
。
如果您是一名 JavaScript 开发人员,想要跨入新兴的加密货币领域,本文将为您提供入门所需的技能。 或者,如果您对加密货币世界的运作方式感到好奇,那么本教程可能有助于回答您的一些问题。
推荐阅读: Drew McLellan 理解子资源完整性
先决条件
要成功学习本教程,您需要具备以下条件:
- Node.js 安装在您的机器上。 你可以在这里下载;
- 代码编辑器,例如 Visual Studio Code、Sublime Text 或任何其他。
让我们开始吧…
什么是区块链?
区块链是为比特币和以太坊等数字货币提供动力的技术。 它是一种创新的分布式公共分类账技术,可维护不断增长的记录列表,称为块,这些记录使用加密技术安全连接。
术语区块链因其保存交易数据的方式而得名,即在彼此连接以创建链的块中。 区块链的规模随着所进行交易数量的增加而增长。
任何有效的交易数据都会记录到区块链网络中,该网络受参与者规定的点对点规则的约束。 例如,这些数据可能包含区块的“价值”,例如数字货币、交易记录(例如各方交换商品和服务的时间)或权利特权,例如链记录所有权信息的时间。
除了交易数据,每个区块都可能包含自己的加密哈希(唯一标识符或数字足迹)、自己的 nonce 值(在加密计算中使用一次的任意随机数)、前一个区块的哈希和最近的时间戳经验证的交易。
由于每个新块都应该指向前一个块,如果一个块被合并到链中而不包含最后一个块的正确哈希,它可能会导致整个区块链无效。 这种不变性是区块链安全的关键。
此外,通常会应用各种类型的共识协议来维护区块链的真实性。 共识确保所有参与者都同意网络验证的交易。
例如,一种常用的共识协议是工作量证明,其目的是识别一个数字,在完成一定量的计算工作后找到一个复杂数学问题的解决方案。
证明工作的主要思想是区块链网络中的任何参与者都应该发现这个数字难以识别但易于验证。 因此,它阻止了垃圾邮件和篡改区块链结构的行为。
在大多数加密货币的情况下,向区块链添加新区块需要求解一个复杂的数学方程,随着区块链的增长,难度会随着时间的推移而增加。 因此,任何通过解决这个问题证明他们已经完成工作的人都会在被称为“挖矿”的过程中获得数字货币的补偿。
如何创建块
现在,在介绍了区块链技术及其工作原理之后,让我们看看如何将这些概念应用于创建区块。 如前所述,区块是相互链接以形成区块链的东西。
为了创建smashingCoin
货币,我将使用在ES6 中引入的JavaScript 类。
准备好?
让我们把手弄脏……
这是CryptoBlock
类的代码:
const SHA256 = require('crypto-js/sha256'); class CryptoBlock{ constructor(index, timestamp, data, precedingHash=" "){ this.index = index; this.timestamp = timestamp; this.data = data; this.precedingHash = precedingHash; this.hash = this.computeHash(); } computeHash(){ return SHA256(this.index + this.precedingHash + this.timestamp + JSON.stringify(this.data)).toString(); } }
正如您在上面的代码中所见,我创建了CryptoBlock
类并向其添加了constructor()
方法——就像在任何其他 JavaScript 类中所做的一样。 然后,为了初始化其属性,我将以下参数分配给constructor
方法:
index | 它是一个唯一编号,用于跟踪整个区块链中每个区块的位置。 |
timestamp | 它记录每个已完成事务的发生时间。 |
data | 它提供有关已完成交易的数据,例如发件人详细信息、收件人详细信息和交易数量。 |
precedingHash | 它指向区块链中前一个区块的哈希值,这对于维护区块链的完整性很重要。 |
此外,我使用computeHash
方法根据其属性计算块的哈希,如上面的数据所示。
如您所见,我导入了 crypto-js JavaScript 库并使用其crypto-js/sha256
模块来计算每个块的哈希值。 由于模块返回一个数字对象,我使用toString()
方法将其转换为字符串。
要将 crypto-js 库添加到您的项目中,请转到终端并运行以下命令以使用npm
安装它:
npm install --save crypto-js
运行上述命令后,包含库和其他基本文件的节点模块目录将添加到您的项目文件夹中。
如何创建区块链
如前所述,区块链技术基于所有区块相互链接的概念。 所以,让我们创建一个CryptoBlockchain
类来负责处理整个链的操作。 这是橡胶将与道路相遇的地方。
CryptoBlockchain
类将使用完成不同任务的辅助方法来维护区块链的操作,例如创建新块并将它们添加到链中。
这是CryptoBlockchain
类的代码:
class CryptoBlockchain{ constructor(){ this.blockchain = [this.startGenesisBlock()]; } startGenesisBlock(){ return new CryptoBlock(0, "01/01/2020", "Initial Block in the Chain", "0"); } obtainLatestBlock(){ return this.blockchain[this.blockchain.length - 1]; } addNewBlock(newBlock){ newBlock.precedingHash = this.obtainLatestBlock().hash; newBlock.hash = newBlock.computeHash(); this.blockchain.push(newBlock); } }
让我谈谈构成CryptoBlockchain
类的每个辅助方法的作用。
1.构造方法
此方法实例化区块链。 在构造函数中,我创建了区块blockchain
属性,它引用了一个块数组。 请注意,我向它传递了startGenesisBlock()
方法,该方法在链中创建了初始块。
2. 创建创世块
在区块链中,创世块是指在网络上创建的第一个块。 每当一个块与链的其余部分集成时,它应该引用前一个块。
相反,在这个初始块的情况下,它没有任何前面的块可以指向。 因此,创世块通常被硬编码到区块链中。 这样,可以在其上创建后续块。 它的索引通常为 0。
我使用startGenesisBlock()
方法来创建创世块。 请注意,我使用precedingHash
创建的CryptoBlock
类创建了它,并传递了index
、 timestamp
、 data
和 previousHash 参数。
3. 获取最新区块
获取区块链中的最新区块有助于确保当前区块的哈希指向前一个区块的哈希——从而保持链的完整性。
我使用了obtainLatestBlock()
方法来检索它。
4.添加新块
我使用addNewBlock()
方法将新块添加到链中。 为了做到这一点,我将新块的前一个哈希值设置为链中最后一个块的哈希值——从而确保链是防篡改的。
由于每次新计算都会改变新块的属性,因此再次计算其加密哈希非常重要。 更新其哈希后,新块被推入区块链数组。
实际上,由于已经放置了几项检查,因此向区块链添加新块并不容易。 尽管如此,对于这种简单的加密货币,足以证明区块链的实际运作方式。
测试区块链
现在,让我们测试我们的简单区块链,看看它是否有效。
这是代码:
let smashingCoin = new CryptoBlockchain(); smashingCoin.addNewBlock(new CryptoBlock(1, "01/06/2020", {sender: "Iris Ljesnjanin", recipient: "Cosima Mielke", quantity: 50})); smashingCoin.addNewBlock(new CryptoBlock(2, "01/07/2020", {sender: "Vitaly Friedman", recipient: "Ricardo Gimenes", quantity: 100}) ); console.log(JSON.stringify(smashingCoin, null, 4));
正如您在上面的代码中看到的,我创建了CryptoBlockchain
类的一个新实例并将其命名为smashingCoin
。 然后,我使用一些任意值将两个块添加到区块链中。 在data
参数中,我使用了一个对象并添加了发件人详细信息、收件人详细信息和交易数量。
如果我在终端上运行代码,这是我得到的输出:
这就是smashingCoin
的样子! 它是一个包含区块blockchain
属性的对象,它是一个包含链中所有块的数组。 如上图所示,每个区块都引用了前一个区块的哈希值。 例如,第二个块引用了第一个块的哈希。 在测试并看到我们的区块链有效之后,让我们添加更多功能来增强smashingCoin
的功能。
如何验证区块链的完整性
如前所述,区块链的一个关键特征是,一旦将一个块添加到链中,就不能在不破坏链其余部分的完整性的情况下对其进行更改。
因此,为了验证区块链的完整性,我将在CryptoBlockchain
类中添加一个checkChainValidity()
方法。
哈希对于确保区块链的有效性和安全性至关重要,因为区块内容的任何更改都将导致产生全新的哈希,并使区块链失效。
因此, checkChainValidity()
方法将使用if
语句来验证每个块的哈希是否已被篡改。 从第一个创建的块开始,它将遍历整个区块链并检查其有效性。 请注意,由于创世块是硬编码的,因此不会被检查。
此外,该方法将验证每两个连续块的哈希是否相互指向。 如果区块链的完整性没有受到损害,则返回 true; 否则,如果出现任何异常,则返回 false。
这是代码:
checkChainValidity(){ for(let i = 1; i < this.blockchain.length; i++){ const currentBlock = this.blockchain[i]; const precedingBlock= this.blockchain[i-1]; if(currentBlock.hash !== currentBlock.computeHash()){ return false; } if(currentBlock.precedingHash !== precedingBlock.hash) return false; } return true; }
如何添加工作证明
如前所述,工作量证明是用于增加挖矿难度或向区块链添加新区块的概念。
在smashingCoin
的情况下,我将使用一种简单的算法来阻止人们轻松生成新块或向区块链发送垃圾邮件。
因此,在CryptoBlock
类中,我将添加另一个名为proofOfWork().
本质上,这个简单的算法识别一个数字,作为difficulty
属性传递,这样每个块的哈希都包含对应于这个difficulty
级别的前导零。
确保每个块的哈希以difficulty
级别中设置的零数量开始,这需要大量的计算能力。 难度级别越高,挖掘新区块所需的时间就越多。
此外,我将为每个散列块添加一个随机nonce
值,以便在重新散列时仍然可以满足难度级别限制。
这是代码:
proofOfWork(difficulty){ while(this.hash.substring(0, difficulty) !==Array(difficulty + 1).join("0")){ this.nonce++; this.hash = this.computeHash(); } }
而且,这是更新后的computeHash()
方法,其中包含nonce
变量:
computeHash(){ return SHA256(this.index + this.precedingHash + this.timestamp + JSON.stringify(this.data)+this.nonce).toString(); }
另外,为了在新块的生成中实现工作量证明机制,我将把它包含在addNewBlock()
方法中:
addNewBlock(newBlock){ newBlock.precedingHash = this.obtainLatestBlock().hash; //newBlock.hash = newBlock.computeHash(); newBlock.proofOfWork(this.difficulty); this.blockchain.push(newBlock); }
包起来
以下是使用 Node.js 构建smashingCoin
加密货币的完整代码:
const SHA256 = require("crypto-js/sha256"); class CryptoBlock { constructor(index, timestamp, data, precedingHash = " ") { this.index = index; this.timestamp = timestamp; this.data = data; this.precedingHash = precedingHash; this.hash = this.computeHash(); this.nonce = 0; } computeHash() { return SHA256( this.index + this.precedingHash + this.timestamp + JSON.stringify(this.data) + this.nonce ).toString(); } proofOfWork(difficulty) { while ( this.hash.substring(0, difficulty) !== Array(difficulty + 1).join("0") ) { this.nonce++; this.hash = this.computeHash(); } } } class CryptoBlockchain { constructor() { this.blockchain = [this.startGenesisBlock()]; this.difficulty = 4; } startGenesisBlock() { return new CryptoBlock(0, "01/01/2020", "Initial Block in the Chain", "0"); } obtainLatestBlock() { return this.blockchain[this.blockchain.length - 1]; } addNewBlock(newBlock) { newBlock.precedingHash = this.obtainLatestBlock().hash; //newBlock.hash = newBlock.computeHash(); newBlock.proofOfWork(this.difficulty); this.blockchain.push(newBlock); } checkChainValidity() { for (let i = 1; i < this.blockchain.length; i++) { const currentBlock = this.blockchain[i]; const precedingBlock = this.blockchain[i - 1]; if (currentBlock.hash !== currentBlock.computeHash()) { return false; } if (currentBlock.precedingHash !== precedingBlock.hash) return false; } return true; } } let smashingCoin = new CryptoBlockchain(); console.log("smashingCoin mining in progress...."); smashingCoin.addNewBlock( new CryptoBlock(1, "01/06/2020", { sender: "Iris Ljesnjanin", recipient: "Cosima Mielke", quantity: 50 }) ); smashingCoin.addNewBlock( new CryptoBlock(2, "01/07/2020", { sender: "Vitaly Friedman", recipient: "Ricardo Gimenes", quantity: 100 }) ); console.log(JSON.stringify(smashingCoin, null, 4));
如果我在终端上运行代码,这是我得到的输出:
如上图所示,哈希现在以四个零开头,这与工作量证明机制中设置的难度级别相对应。
结论
而已! 这就是您可以使用 Node.js 构建简单的加密货币区块链的方式。
当然, smashingCoin
加密货币远未完成。 事实上,如果你在不做更多改进的情况下发布它,它就不太可能满足当前市场对安全、可靠和直观的数字货币的需求——让你成为唯一使用它的人!
尽管如此,我希望本教程为您提供了一些基本技能,让您在激动人心的密码世界中涉足。
如果您有任何意见或问题,请在下方发布。
更多资源
- “区块链 101”,CoinDesk
- “比特币:点对点电子现金系统”,中本聪,Bitcoin.org