尝试通过制作区块链来了解区块链!
介绍
比特币和加密货币最近引起了不小的轰动。我对加密货币的转变感到相当失望,它从一个令人惊奇的概念变成了另一种快速赚钱的方式(或者说,不是……)。
但我对加密货币背后的技术,尤其是区块链的概念非常感兴趣。这个概念非常吸引人,而且不仅限于比特币之类的。我们可以想象这项技术将有多么广泛的应用。因此,我决定以开发者的视角,编写一个区块链(或者说,我认为是区块链)的代码,以便更好地理解它。
一个简单的项目
那么,我们需要什么来创建一个非常简单的区块链?
- 一个区块
区块链由区块构成。在我们的例子中,一个区块由日期、索引、一些数据(在本例中是一条消息)以及前一个区块的哈希值组成。
- 密码学
为了确保信息安全,我们需要加密数据。在我们的小项目中,我们将使用 js-sha256 包。此过程将创建一个 64 个字符的字符串。最终,我们的区块链将由一系列哈希值组成,每个哈希值由 64 个字符组成。正如我之前所说,我们使用前一个区块的哈希值来加密新区块(这就是我们称之为“链”的原因)。
- 难度和随机数
我们不会为每个区块创建一个哈希值就完事了。哈希值必须有效。在我们的例子中,如果哈希值的前四个字符为 0,则该哈希值有效。如果哈希值以“0000……”开头,则被视为有效。这被称为难度。难度越高,获取有效哈希值所需的时间就越长。
但是,如果哈希值第一次无效,那么我们使用的数据肯定发生了变化,对吗?如果我们反复使用相同的数据,就会反复得到相同的哈希值,我们的哈希值永远无效。你说得对,我们在哈希值中使用了“nonce” (随机数)。它只是一个数字,每次哈希值无效时,我们都会递增它。我们获取数据(日期、消息、之前的哈希值、索引)和一个值为 1 的随机数。如果用这些数据得到的哈希值无效,我们会尝试使用值为 2 的随机数。然后,我们递增随机数,直到得到一个有效的哈希值。
- 创世区块
我们的链中必须有一个第一个区块,称为创世区块。当然,这个区块不能使用前一个区块的哈希值,因为它不存在。我们只需给它一些任意数据来创建它的哈希值。
这几乎就是我们的区块链所需要的。
方法
我们需要一些方法来制作功能性的区块链:
- 初始化我们的区块链 => 创建创世块
- 对我们的区块进行哈希处理 => 负责创建有效哈希值的函数
- 检查哈希的有效性 => 我们的哈希是否以“OOOO”开头?
- 获取最后一个哈希值 => 我们需要前一个哈希值来创建新的区块
- 添加新区块 => 如果我们想要一个链,我们需要在某个时刻这样做
太棒了!!
现在让我们开始编码。
对于这个小项目,我将创建两个文件,一个名为index.js,另一个名为block.js。第二个文件将包含我们创建区块链的小模块。它很简单,让我们看一下:
const sha256 = require('js-sha256').sha256
const blockchain = (function(){
const blocks = []
const initBlockchain = () => {
const data = 'Hello World!'
const timestamp = new Date()
const previousHash = 0
const index = 0
hashBlock(data, timestamp, previousHash, index)
}
const hashBlock = (data, timestamp, prevHash, index) => {
let hash = '', nonce = 0
while( !isHashValid(hash) ){
let input = `${data}${timestamp}${prevHash}${index}${nonce}`
hash = sha256(input)
nonce += 1
}
console.log(nonce)
blocks.push(hash)
}
const getLastHash = blocks => blocks.slice(-1)[0]
const isHashValid = hash => hash.startsWith('0000') // Difficulty
const addNewBlock = data => {
const index = blocks.length
const previousHash = getLastHash(blocks)
hashBlock(data, new Date(), previousHash, index)
}
const getAllBlocks = () => blocks
return {
initBlockchain,
getLastHash,
blocks,
getAllBlocks,
addNewBlock
}
})()
module.exports = blockchain
所以,在这个模块中,我有几个方法。在顶部,我导入了处理加密部分的模块。我有一个空数组,用于保存我的区块链的区块,名为block。
initBlockchain:此方法通过创建第一个区块(创世区块)来启动区块链。我为其指定了一个时间戳、一条消息、该区块在区块链中的索引(0)以及一个任意的先前哈希值(因为链中还没有先前的区块)。有了所有这些信息,我现在就可以创建创世区块的哈希值了。
hashBlock:此方法获取所有区块的数据并创建哈希值。如您所见,我们第一次针对特定区块运行该函数时,随机数 (nonce)被设置为 0。我们加密区块,并使用isHashValid检查哈希值是否有效。在本例中,如果前四个字符为 0,则哈希值有效。这被称为难度。我们必须解决这个问题,以确保该区块能够成为区块链的一部分。哈希值有效后,我们将其添加到区块数组中。
addNewBlock:此方法负责创建一个新区块。我们只需要传入消息作为参数,因为其他所有参数(index、previousHash 和 timestamp)都可以在区块链中找到。该方法使用数据调用hashBlock来创建并验证新区块。
getLastHash:我调用此方法获取上一个哈希值。我们总是需要上一个哈希值来创建新区块。
getAllBlocks:仅返回区块链中当前的所有区块
太好了,让我们转到index.js来使用我们的新区块链!
const blockchain = require('./blockchain')
blockchain.initBlockchain()
blockchain.addNewBlock('First new block')
blockchain.addNewBlock('I love blockchains')
blockchain.addNewBlock('Make me a new hash!!')
console.log(blockchain.getAllBlocks())
我们初始化区块链,然后创建三个新区块。运行该命令后,我得到了以下响应:
Initializing the blockchain
139355
30720
68789
51486
[ '0000d87875f12e8c00d60cdfc8c21c4867eb1e732d3bb0e4d60bd0febcfafbaf',
'0000331d80f4e83461bad846e082baa08c5e739edfa19a4880c1dcbe4eed1984',
'00000dcab247410050e357158edc20555cc0110429023fdadb1d8cda3e06da5e',
'0000a16968811cf75c33d877e99f460d396c46b5485f669c8e55b193b862106d' ]
该数组代表四个区块。如你所见,每个区块都以四个零开头,因此每个哈希值都是有效的。如果其中一个哈希值不是以四个零开头,我就能立刻知道该哈希值无效,因此,相应区块中的数据可能不可信。
这里有四个数字:139355、30720、68789、51486 。这些是每个区块的随机数。我把它们打印出来,看看hashBlock函数运行了多少次才得到一个有效的哈希值。
第一个区块,也就是创世区块,运行了 139355 次才得到一个有效的哈希值!第二个区块运行了 30720 次,第三个区块运行了 68789 次,第四个区块运行了 51486 次。
结论
这是一个非常简单的区块链示例。我肯定漏掉了一些东西。我尽量保持简洁,因为嘿,我正在学习!这个小项目让我明白了一些事情:
-
如果一个人决定修改前一个区块,那么她必须修改该区块之后的每个区块。每个区块都继承自其父区块(前一个哈希值),因此试图欺骗区块链似乎很复杂。
-
但如果区块链的大多数用户决定作弊,他们可以修改之前的区块,并让所有用户都同意相应地修改区块链的其余部分。区块链似乎只有大多数人决定遵守规则才能发挥作用。或者,最终可能会出现两条不同的区块链:一条用户决定保留原始数据,另一条用户决定使用修改后的区块链。
-
我听说过比特币在挖矿方面耗费了巨大的算力。挖矿的概念是解决加密数据时的难度问题。你获取交易,然后尝试找到该区块的有效哈希值。作为你努力的奖励,你会获得一些比特币。我只能想象,当区块链变得庞大时,你将消耗多少算力。
嗯,这就是我从中得到的启示。这让我明白了很多。如果我说错了,请随时纠正我!
文章来源:https://dev.to/damcosset/trying-to-understand-blockchain-by-making-one-ce4