深入浅出区块链(4)区块链基础

在前面区块链的架构部分介绍过区块链的产生过程,如下图

blockchain_basic.png

简单来说,区块链就是把(加密)数据存入区块中,经“挖矿”加入整个链条,生成的永久、不可逆向修改的记录。在本节中,将从代码的角度深入理解这一过程。实践是最好的老师,下面将动手实现一个简单的区块链。

为方便讲解区块链基础,在下面开始用Python实现一个简化版的区块链。区块的数据结构如下:

字段 描述
identifier 一个唯一的字符串作为标示
previous_hash 前一个区块的哈希值
data 区块中的数据
nonce 随机数,用来找到正确的哈希值

其中identifier这个唯一的字符串可以用python的uuid库uuid4()生成。previous_hash是在接入区块链的时候赋值。data是区块中写入的任意数据。比较特别的是nonce,它有什么用呢?它的作用很简单,用来找到正确哈希值。

至于为什么要找正确的哈希值,就要讲到区块的有效性。由上图中可以看到,区块链中的区块不是新生成就完成,而是需要通过“挖矿”这一步骤才可以。简单来说,挖矿的本质就是找到一个合适的nonce值使得区块的哈希值有效。

mining.png

挖矿

哈希值是通过哈希算法计算得到的一段二进制值,不同的数据得到不同的哈希值。

# 这里举个简单的例子

# 导入相关类库
import hashlib

# 定义数据
data = "Hello World"

# 计算哈希值
msg = hashlib.sha256()
msg.update(data.encode('utf-8')) # 主要计算前必须将数据转成utf-8
msg.hexdigest()
'a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e'

上面就一个哈希值,如果修改数据,比如添加一个nonce值就可以产生不同的哈希值

nonce = 0
msg = hashlib.sha256()
msg.update(data.encode('utf-8'))
msg.update(str(nonce).encode("utf-8"))
msg.hexdigest()
'1370eeaaba7a6c7a234b1f82cc3b6d013a0088fd5e16408300f05b28b0015463'

在区块链中区块的哈希值必须满足一定条件才能算是有效的区块。这里设定区块的哈希值必须以0000开头才是有效的区块。为了得到以0000开头的哈希值,需要不断的修改nonce(挖矿)直到满足条件。下面定义一个函数来寻找该nonce值。

def mine(data):
    nonce = 0   # nonce初始值为0
    
    # 寻找正确的哈希值
    while True:
        msg = hashlib.sha256()
        msg.update(data.encode("utf-8"))
        msg.update(str(nonce).encode("utf-8"))
        hash_code = msg.hexdigest()
        
        # 如果满足条件,打印相应值并退出循环
        if hash_code.startswith("0000"):
            print("nonce:{}".format(nonce))
            print("hash:{}".format(hash_code))
            break
        # 否则继续寻找
        else:
            nonce += 1
mine("Hello, World")
nonce:104803
hash:000022635160d3ae1c8c90261e0df5eb6538d7f6d42108d6ffdec17b585fb464
mining2.png

上面只是用data和nonce来计算哈希值,实际区块链中还会包含其他字段。下面定义一个区块的数据结构。

实现区块结构

# block.py

import hashlib
import uuid


class Block(object):
    def __init__(self, data=None, previous=None):
        self.identifier = uuid.uuid4().hex   # 产生唯一标示
        self.previous = previous            # 父节点
        if previous:
            self.previous_hash = previous.hash() # 父节点哈希值
        else:
            self.previous_hash = None
        self.data = data                     # 区块内容
        self.nonce = 0                    # nonce值
        
    def hash(self):
        '''
        计算区块的哈希值,包括区块的标示、数据、前一区块的哈希值和nonce值
        '''
        message = hashlib.sha256()
        message.update(self.identifier.encode('utf-8'))
        message.update(str(self.nonce).encode('utf-8'))
        message.update(str(self.data).encode('utf-8'))
        if self.previous:
            message.update(str(self.previous_hash.encode('utf-8')))
        return message.hexdigest()
    
    def refresh_hash(self):
        if self.previous:
            self.previous_hash = previous.hash() # 父节点哈希值
        else:
            self.previous_hash = None
    
    def mine(self):
        '''
            挖矿函数
        '''
        # 初始化nonce为0
        cur_nonce = self.nonce or 0

        # 循环直到生成一个有效的哈希值
        while True:
            if self.hash_is_valid():   # 如果生成的哈希值有效
                break                          # 退出
            else:
                self.nonce += 1   # 若当前哈希值无效,更新nonce值,进行加1操作
                

    def hash_is_valid(self):
        '''
        校验区块哈希值有否有效
        '''
        return self.hash().startswith('0000')

    def __repr__(self):
        return 'Block<identifier: {}>'.format(self.identifier)

以上就是一个区块结构,这里的区块包含一个唯一标识符、父节点的哈希值、nonce值和该区块的内容字段。其中定义一个mine函数用了寻找合适的nonce值。另外定义了一个hash_is_valid函数用来判断这个是否以0000开头,即是否有效。下面对这个区块结构进行初始化。

# 创建一个内容为hello world的内容块

block = Block('Hello, World')
# 区块链的有效性
block.hash_is_valid()
False
# 挖矿,循环直至找到合适的nonce
block.mine()
# 再次检查区块的有效性
block.hash_is_valid()
True

至此,第一个有效的区块生成完成,下面开始实现区块链。

实现区块链结构

class BlockChain(object):
    def __init__(self):
        self.head = None   # 指向最新的一个区块
        self.blocks = {}   # 包含所有区块的一个字典

    '''
        添加区块函数
    '''
    def add_block(self, new_block):
        new_block.previous = new_block.previous
        new_block.mine()

        self.blocks[new_block.identifier] = block
        self.head = new_block

    def __repr__(self):
        num_existing_blocks = len(self.blocks)
        return 'Blockchain<{} Blocks, Head: {}>'.format(
            num_existing_blocks,
            self.head.identifier if self.head else None
        )

定义好区块链结构后,下面就开始初始化一条区块链。

# 初始化
chain = BlockChain()

# 打印
chain
Blockchain<0 Blocks, Head: None>
# 添加区块
chain.add_block(block)

# 打印
chain
Blockchain<1 Blocks, Head: 1294d073adf5476db720d7b2e752d62b>
# 添加更多的区块

for i in range(6):
    new_block = Block(i)
    chain.add_block(new_block)
    
# 打印
chain
Blockchain<7 Blocks, Head: 7f8291bedac845af8637112136c41fd3>
for i,v in chain.blocks.items():
    if v.hash_is_valid():
        print("{} is valid".format(k))
    else:
        print("\033[0;31m{} is invalid\033[0m")
a70c66dec8154a37864838638bc1ef71 is valid
a70c66dec8154a37864838638bc1ef71 is valid
a70c66dec8154a37864838638bc1ef71 is valid
a70c66dec8154a37864838638bc1ef71 is valid
a70c66dec8154a37864838638bc1ef71 is valid
a70c66dec8154a37864838638bc1ef71 is valid
a70c66dec8154a37864838638bc1ef71 is valid

以上就是一个简单区块链,可以看到当前的区块都是有效的。但是,值的注意的是,每个区块包括前一个区块的哈希值,所以,当区块链中一个区块被改变后,这个区块的哈希就会改变,从而影响到这块区块之后的区块。

# 比如改变第一个区块的内容
block.data = "Modify Data"
block.hash_is_valid()
False
invalid_block.png

这将使这个区块哈希值改变并导致区块无效,并且,这个修改将影响之后的区块,因为之后的区块中的previous_hash依赖于前面的区块。改变如下:

for k,v in chain.blocks.items():
    # 更新区块
    v.refresh_hash()
    if v.hash_is_valid():
        print("{} is valid".format(k))
    else:
        print("\033[0;31m{} is invalid\033[0m".format(k))
�[0;31mb41171e618334412871495fb6d6f1a28 is invalid�[0m
�[0;31m1294d073adf5476db720d7b2e752d62b is invalid�[0m
�[0;31m97dd4e3905584c18ab2028c92b3afaad is invalid�[0m
�[0;31m02cb150841d6420ead9388d0ecfa3eef is invalid�[0m
�[0;31m7f8291bedac845af8637112136c41fd3 is invalid�[0m
�[0;31m2cfe78eac1db48f099a10ea968d468dd is invalid�[0m
�[0;31m2119e357b03442e1bbc12f355ffdf00c is invalid�[0m

可以看到由于前面区块的改变导致之后的区块也无效了。

invalid_block2.png

以上模拟了单个用户的区块链操作,在实际过程中挖矿和校验的行为由区块链中其他用户进行的,并根据挖矿产生的劳动得到对应的报酬。这样就保证了单个用户无法修改区块链中的数据。并且这个过程是通过加密算法进行的,这就实现了区块链的去信任。

blockchain.png

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容