前言

最近在学习区块链,因为论文中可能会用到,然后发现一篇知乎专栏文章把区块链介绍的很简单也很好,于是乎准备复现一波。

文章来自此处《从零开始创建一个区块链》.

正文

1. 区块链有什么了不起?

区块链在当今市场上越来越受到欢迎。Amazon、Microsoft、Google和其他巨头公司已经开始提供区块链技术的相关服务。很多人经常将区块链与比特币等加密货币联系起来,但很多时候,事实并非如此,区块链从某种角度上来说只是一个“记事本”,而记事本就是一个“数据库”,所以它除了充当加密货币的记账本以外,还能充当一个“数据库”。

区块链允许我们创建安全的、公开的、分散的“数据库”来储存任何类型的信息。在区块链之前,大量的数据必须由某种形式的中间人或者中介机构进行存储和验证。但是作为一个安全人员,我们要知道,机构永远都是不可信的,我们需要把重要的资料保存在自己的手里。我们只是相信他们不会以任何方式篡改或者滥用我们的数据,但是事实真的是这样吗?或者即使他们不会篡改,但是如果他们因为自身安全等级不够遭到黑客的入侵导致了各种各样的数据泄露呢?

区块链技术的核心思想是利用密码学和复杂的计算机算法,创造出一套安全透明的方法,将这些中介机构剔除出去,让数据由一个巨大的开放的服务器网络进行存储和验证。

2. 数据是如何存储的?

区块是什么?

在此之前我们需要熟悉一个东西,叫做哈希函数(又叫单向散列函数)。它接收一个任意长度的字符串作为输入,并返回一个固定长度的字符串,且看起来非常随机。每一个字符串都有它自己的哈希值,而且这个哈希值总是相同的。且任何一点微小的改动,都会导致输出的哈希值完全不同。

那么我们现在再来看看区块里面有什么,其实区块里面的东西非常的简单:

  1. 当前区块的数据
  2. 前一个区块的哈希值
  3. ((当前区块的数据)+(前一个区块的哈希值))的哈希值

其中我们((当前区块的数据)+(前一个区块的哈希值))的哈希值其实也就是当前区块的哈希值。由此我们可以看出,我们的数据一旦记录,并且一旦被后面的区块引用,这个数据将是非常难改动的,因为你会需要改动所有因此数据改动而受到影响的所有区块。也就是说,你必须重写这个数据之后的整一个区块链上的所有区块。

当我们写入python的时候,其实就是这个样子的:

class Block():
    def __init__(self, data, previous_block_hash):
        self.data = data
        self.prev_block_hash = previous_block_hash
        self.calculate_valid_hash()
        
    def is_hash_valid(self, current_block_hash):
        return (current_block_hash.startswith('0' * 3))
    
    def calculate_valid_hash(self):
        current_block_hash = ''
        nonce = 0
        
        while (not self.is_hash_valid(current_block_hash)):
            tmp = self.to_string_pre() + str(nonce)
            current_block_hash = sha256(tmp.encode()).hexdigest()
            nonce += 1
            
        self.hash = current_block_hash
        self.nonce = nonce - 1
        
    def to_string_pre(self):
        return "{0}\t{1}".format(self.data, self.prev_block_hash)
    
    def to_string(self):
        return "{0}\t{1}\t{2}".format(self.data, self.hash, self.nonce)

其中Block为我们的区块类,init()中我们可以发现它存了dataprevious_block_hash和一个计算当前hash的函数calculate_valid_hash()

同时我们有两个函数to_string_pre()to_string(),一个会打印data和上一个区块的hash值,然而另一个会打印data和当前区块的hash值。

3. 工作证明

我们在上一章了解到,其实用hash函数算一个文件或者数据的值是很快的事情,那么平时我们怎么会觉得挖矿会用这么大的算力呢?这就涉及到区块链本身的一些规则了。

我们在此之前需要介绍hash函数的另一个特性,逆向计算的难度相当之大。这意味着,算出一个数据的hash值是相当简单的,但是从hash值推出原始的数据则是相当难的。因为对于SHA256而言,要从hash值逆向推出原始数据,需要遍历2^256,这个对于计算机而言是相当困难的。

因此,为了防止冒名顶替者过快的修改整个区块链,我们会增加生成一个区块的难度,方法其实很简单,规则就是:数据的hash值必须以x个零开头,此处x是一个阿拉伯数字。

这就是工作证明背后的概念,也是区块链为什么如此难篡改的原因。该规则意味着如果要创建一个新的区块需要大量的时间,如果你要更改以前的区块,则将同时要计算出此区块之后所有的区块。

4. 如何创建新的区块

因此如何创建新的区块的方法也出来了,最简单的方法就是在dataprev_block_hash之后加上一个随机数nonce。我们一直算hash值,直到算出来的值满足区块链的hash值规则为止(以x个零开头)。

这个过程也就是我们Block类中calculate_valid_hash()所要做的动作,而is_hash_valid()就是看生成的hash值满不满足我们的区块链hash值规则。

5. 创建区块链类

有了一系列的区块,我们需要将区块串起来形成区块链,这一步也简单,直接放python代码:

class Blockchain():
    def __init__(self):
        self.blocks = []
        self.init_genesis_block()
        
    def init_genesis_block(self):
        data = "Genesis"
        prev_hash = '0'*64
        genesis_block = Block(data, prev_hash)
        self.blocks.append(genesis_block)
        
    def add_new_block(self, data):
        prev_hash = self.blocks[-1].hash
        block = Block(data, prev_hash)
        self.blocks.append(block)
        
    def is_blockchain_valid(self):
        i = 0
        cur_hash = self.blocks[0].prev_block_hash
        for block in self.blocks:
            tmp = "{0}\t{1}{2}".format(block.data, cur_hash, block.nonce)
            tmp_hash = sha256(tmp.encode()).hexdigest()
            ori_hash = self.blocks[i].hash
            print(tmp_hash)
            if tmp_hash == ori_hash:
                i += 1
            cur_hash = tmp_hash
        if i == len(self.blocks):
            print('Original Blockchain!')
        else:
            print('Modified Blockchain!')

我们发现区块链类中第一位有一个genesis_block,这也是我们的创世区块,我们创世区块一般data随机生成即可,且prev_block_hash为64个0的hash值(一个Hex字符表示4位(4bit),所以64个Hex字符是64*4=256位,64个0刚好就是SHA256,256位)。

6. 测试

6.1 生成Hash值

代码

blockchain = Blockchain()
blockchain.add_new_block("First Block")
blockchain.add_new_block("Second Block")
blockchain.add_new_block("Third Block")
for block in blockchain.blocks:
    print(block.to_string())

结果

Genesis	000e1695a94ed18f410403027ba17fbb88353d6270e553e9ffe747d4b1b71d81	202
First Block	000ff3dff04dbb06c2071bb7375dad9bef018db2a3ed38febfdfd6f8635bc850	1549
Second Block	000772cce3a09a0f50ff9d1cad8ed0de1b213d65ce68863db089b5621c5858b7	6763
Third Block	000f422517bc56fe44377cd2d8368b5d7934b4aa6ec648484e0d8f4fd5df2fd7	3399

我们测试一下,验证此为原始Hash值:

代码:

blockchain.is_blockchain_valid()

打印:

000e1695a94ed18f410403027ba17fbb88353d6270e553e9ffe747d4b1b71d81
000ff3dff04dbb06c2071bb7375dad9bef018db2a3ed38febfdfd6f8635bc850
000772cce3a09a0f50ff9d1cad8ed0de1b213d65ce68863db089b5621c5858b7
000f422517bc56fe44377cd2d8368b5d7934b4aa6ec648484e0d8f4fd5df2fd7
Original Blockchain!

6.2 改变第二个区块

我们先将First区块的First Block 改为 Fiist Block

代码:

# Change First Block
blockchain.blocks[1].data = 'Fiist Block'
blockchain.is_blockchain_valid()

发现除了第一个Genesis没改以外,First Block以后的全部都改了。第一个Genesis没改只因为,区块的改动不会影响当前区块以前的区块,但是会影响当前区块所有之后的区块,所以其防篡改能力是非常非常非常强的!

打印:

000e1695a94ed18f410403027ba17fbb88353d6270e553e9ffe747d4b1b71d81
11c86bc5547461e11c605155e9045fbc1d55c2cf752757f8bd4a93e4879cf191
ad987374492d97a823e0429246b53e1cd75a0494f3c5b6414d4a7267f8ca6d6e
4aca8e9bcd5086d738db73e58e3252184f031ead9f3cd8e518e36ad769dc6eec
Modified Blockchain!

总结

区块链其实不仅仅可以做加密货币。其本质是一个数据库,能够完成所有数据库能够完成的事情!同时又安全、防篡改、开放、透明!

ᕙ(`▿´)ᕗ嘿嘿~!

参考

[1] 从零开始创建一个区块链

Q.E.D.


立志做一个有趣的碳水化合物