动手学区块链学习笔记(二):区块链以及工作量证明算法

news2024/12/23 6:11:26

引言

紧接上文,在介绍完区块链中的加密解密以及公钥私钥等算法后,本篇开始正式进入区块链概念与一个简单区块链系统的实现过程介绍。

区块链技术介绍

什么是区块链?

区块链,就是一个又一个区块组成的链条。每一个区块中保存了一定的信息,它们按照各自产生的时间顺序连接成链条。这个链条被保存在所有的服务器中,只要整个系统中有一台服务器可以工作,整条区块链就是安全的。这些服务器在区块链系统中被称为节点,它们为整个区块链系统提供存储空间和算力支持。如果要修改区块链中的信息,必须征得半数以上节点的同意并修改所有节点中的信息,而这些节点通常掌握在不同的主体手中,因此篡改区块链中的信息是一件极其困难的事。相比于传统的网络,区块链具有两大核心特点:一是数据难以篡改、二是去中心化。基于这两个特点,区块链所记录的信息更加真实可靠,可以帮助解决人们互不信任的问题。
来源:百度百科

总结一句话就是,区块链本质上是一个去中心化的数据库。而它能提取以下几个特点:

  1. 信息不可修改,添加到区块链中的信息无法再被修改;
  2. 只支持「增」和「查」操作,不同于传统的数据库技术支持「增、删、查、改」,区块链技术只支持「增」操作(即往区块链里的添加区块信息),和「查」操作(即查询区块链里的区块信息);而不支持「删」和「改」操作;
  3. 没有权限限制,任何加入区块链网络的节点都有权限「增」和「查」区块信息。

更加通俗的理解,可以查看新华网在2019年发布的刷屏的区块链究竟是什么?你想知道的都在这里!

这里直接讲区块链的区块结构,见下表为:

数据项字节字段说明
Magic NO4魔数常数0xD9B4BEF9
Blocksize4区块大小用字节表示的该字段之后的区块大小
Blockheader80区块头组成区块头的几个字段,描述区块的元数据
Transaction counter1-9交易计数器该区块包含的交易数量,包含coinbase交易,描述紧跟在该域后面的交易数据的数目
Transactions不定交易记录在区块里的交易信息,使用原生的交易信息格式,并且交易在数据流中的位置必须与Merkle树的叶子节点顺序一致

其中Blocksize和Magic NO都是描述区块链的一个量词,余下的三个概念就构成了区块链:

其中区块头的结构为:

大小域名描述
4 字节Version版本号,升级了软件并指定了新版本
32 字节Previous Block Hash与本区块形成链的前一区块的散列值,更准确的讲是前一区块的区块头的散列值
32 字节Merkle Root Hash这个域的准确意义解释起来稍微有点复杂,暂时可以理解为区块体的散列值
4 字节Timestamp可以简单理解为该区块被创建时的 Unix 时间戳(即从 UTC 1970 年 1 月 1 日 0 时 0 分 0 秒起至现在的总秒数)
4 字节Difficulty Target难度调整的一个目标值,后续工作量介绍
4 字节Nonce为了找到满足难度目标所设定的随机数,为了解决32位随机数在算力飞升的情况下不够用的问题,规定时间戳和coinbase交易信息均可更改,以此扩展nonce的位数

在了解比特币系统中的区块头结构中 Previous Block HashMerkle Root Hash 这两个域的大致意义后,区块如何形成区块链的具体细节便清晰了。

区块链的结构有以下两个主要特点:

  • 区块链中的每个区块都通过 Previous Block Hash 记录了前一区块的区块头的散列值;

  • 区块链中的每个区块的区块头都通过 Merkle Root Hash 记录了该区块的区块体的散列值。

在这里插入图片描述

这种结构和数据结构中的链表是非常相似的,见下图所示:

在链表中,初始结点为头结点,而区块链中,初始的块则叫做创世区块(Genesis Block),链表的头结点指向的为Null,因为它本身就是head,创世块同样,它没有前置区块,所以它的Previous Block Hash指向的值便为空。

实现区块类与区块链类

区块类

基于上一节对于区块链结构的分析,下面我们可以使用 Python 中的 Block 类来实现我们第一个版本的区块链的区块了。Block 类包含以下几个域:

字段解释
Timestamp当前时间戳,也就是区块创建的时间
PrevBlockHash前一个块的哈希,即父哈希
Hash当前块的哈希
Data区块存储的实际有效信息,也就是交易

用代码表示为:

class Block(object):
    def __init__(self, data, prev_block_hash=''):
        self.timestamp = int(time.time())
        self.prev_block_hash = prev_block_hash
        self.data = data
        self.data_hash = hashlib.sha256(data.encode('utf-8')).hexdigest()

代码中prev_block_hash=''相当于在申明类时,就自动创建了一个创世区块,并且它指向了在python中代表空的一种字符,使用hashlib中的sha256来计算区块体和区块头的散列值。

目前,我们仅取了 Block 结构的部分字段(Timestamp, DataPrevBlockHash),并将它们相互拼接起来,然后在拼接后的结果上计算一个 SHA-256,就得到了一个新的哈希值,这与比特币的实现方式也是一致的:

class Block(object):
    ......
    def hash(self):
        data_list = [str(self.timestamp), self.prev_block_hash, self.data_hash]
        return hashlib.sha256(''.join(data_list).encode('utf-8')).hexdigest()
    ......

为了能够以更好的可读性的打印出 Block 类,我们给它加上 __repr__ 方法:

class Block(object):
    ......
    def __repr__(self):
        s = 'Block(Hash={}, TimeStamp={}, PrevBlockHash={}, Data={}, DataHash={})'
        return s.format(self.hash(), self.timestamp, self.prev_block_hash,
                self.data, self.data_hash)

至此,一个区块类就完成了,可以将其理解为一种数据结构,跟单链表相似,但是功能与携带信息比单链表更多。

区块链类

如果说区块类为单节点行为,而区块链类相当于赋予了该类与其它节点通信的权利,区块链类为实现区块链整个系统的主要的一个类,所以这里直接给出最简版本为:

from block import Block
from datetime import datetime


class BlockChain(object):
    def __init__(self):
        print('Created a new blockchain.\n')
        self.chain = []
        genesis_block = Block('This is a genesis block')
        self.chain.append(genesis_block)

    def add_block(self, data):
        new_block = Block(data, self.chain[-1].hash())
        self.chain.append(new_block)

    def print_chain(self):
        for block in self.chain:
            print('Block Hash: {}'.format(block.hash()))
            print('Prev Block Hash: {}'.format(block.prev_block_hash))
            print('TimeStamp: {}'.format(datetime.fromtimestamp(block.timestamp)))
            print('Data: {}'.format(block.data))
            print('')

至此,我们创建一个main函数直接去调用上述代码为:

from block import Block
from blockchain import BlockChain


if __name__ == '__main__':
    # 创建区块链
    block_chain = BlockChain()

    # 添加第一个区块到区块链中
    block_chain.add_block('Send 1 BTC to Ivan')
    # 添加第二个区块到区块链中
    block_chain.add_block('Send 2 more BTC to Ivan')

    # 打印整个区块链的信息
    block_chain.print_chain()

输出为:
在这里插入图片描述

这里初步实现了区块链的功能,但为什么还需要使用GPU以及分布式等更多设备呢?那就是下面要讲的矿工核心算法。

工作量证明算法

在上一节,我们构造了一个非常简单的数据结构 – 区块,它也是整个区块链数据库的核心。目前所完成的区块链原型,已经可以通过链式关系把区块相互关联起来:每个块都与前一个块相关联。

但是,依照上述代码实现的区块链有一个巨大的缺陷:向链中加入区块太容易,也太廉价了。区块链的一个关键点就是,一个人必须经过一系列困难的工作,才能将数据放入到区块链中。正是由于这种困难的工作,才保证了区块链的安全和一致。此外,完成这个工作的人,也会获得相应奖励(这也就是通过挖矿获得币)。

这个机制与生活现象非常类似:一个人必须通过努力工作,才能够获得回报或者奖励,用以支撑他们的生活。在区块链中,是通过网络中的参与者(矿工)不断的工作来支撑起了整个网络。矿工不断地向区块链中加入新块,然后获得相应的奖励。在这种机制的作用下,新生成的区块能够被安全地加入到区块链中,它维护了整个区块链数据库的稳定性。值得注意的是,完成了这个工作的人必须要证明这一点,即他必须要证明他的确完成了这些工作。

整个 “努力工作并进行证明” 的机制,就叫做工作量证明(proof-of-work)。

工作量证明流程

工作量证明的核心思想就是给区块链添加新区块的这一操作设置一定的难度。谁都有权限往区块链添加新区块,但并不是谁都有能力这么做,而必须付出一定的代价,这个代价就是工作量证明。

怎么给「区块链添加新区块」的这一操作设置难度呢?其实也很简单,就是规定新区块的区块头的散列值必须满足某种特征。由于散列值具有「单向性」和「雪崩效应」,因此计算机只能通过穷举计算的方法来获得具备某种特征的散列值,而工作量证明算法也就是穷举计算散列值的过程:

这个穷举计算散列值的过程在比特币系统中也被称为挖矿。比特币的实现要求的新区块的区块头散列值特征是散列值必须是一定数目的前导 0前导 0 的数目被称为挖矿的难度,该数值越大表示挖矿的难度也就越大(即要找到能够加入比特币区块链的新区块的时间就越长)。比特币区块头结构中,有两个域与挖矿相关:

大小域名描述
4 字节Difficulty Target表示当前挖矿的难度,但该值不是直接表示前导 0 的数目,而是一个通过特殊编码处理的值,计算方法见如下图
4 字节Nonce为了找到满足难度目标所设定的随机数,为了解决32位随机数在算力飞升的情况下不够用的问题,规定时间戳和coinbase交易信息均可更改,以此扩展nonce的位数

关于Difficulty Target,表格里已经解释得很清楚,需要注意的是,这里的nonce是区块中的nonce,主要是调整挖矿难度;还有一种是每笔交易中nonce,每个外部账户(私钥控制的账户)都有一个nonce值,从0开始连续累加,每累加一次,代表一笔交易。关于后者,会之后介绍。

所以我们可以更新区块类中的哈希方法,将Nonce加入列表:

import time
import hashlib


class Block(object):
    def __init__(self, data, prev_block_hash=''):
        self.timestamp = int(time.time())
        self.prev_block_hash = prev_block_hash
        self.data = data
        self.nonce = 0 # 添加 nonce 成员,初始值为 0
        self.data_hash = hashlib.sha256(data.encode('utf-8')).hexdigest()

    def hash(self): # 计算散列值时,同样加入 nonce值
        data_list = [str(self.nonce),str(self.timestamp),
                    self.prev_block_hash, self.data_hash]
        return hashlib.sha256(''.join(data_list).encode('utf-8')).hexdigest()

    def __repr__(self): # 打印输出,同样加入 nonce值
        return 'Block(Hash={}, TimeStamp={}, PrevBlockHash={}, Nonce={}, \
        Data={}, DataHash={})'.format(self.hash(), self.timestamp,
        self.prev_block_hash, self.nonce, self.data, self.data_hash)

然后定义工作量的noncedifficulty_bits

class ProofOfWork(object):
    MAX_NONCE = sys.maxsize

    def __init__(self, difficulty_bits=12):
        self._target = 1 << (256-difficulty_bits)

这里的MAX_NONCE是用于限定在求解 nonce 值时的上限,sys.maxsize与操作系统有关,如果是32位的操作系统,那么该值将为2^31-1,即2147483647。如果是64位, 将为2^63-1,即9223372036854775807

difficulty_bits的计算规则如下图:
在这里插入图片描述
可能看到左边的1 << (256 - n),就知道这是移位的符号,学过计算机组成原理的,还能说出具体的操作,为算术左移:依次左移n位,尾部补0,最高的符号位保持不变。
在这里插入图片描述

这就是工作量类的核心,也是证明是否付出劳力的两道质检工序,代码为:

    def mine_block(self, data, prev_hash=''):
        tmp_block = Block(data, prev_hash)
        while tmp_block.nonce<ProofOfWork.MAX_NONCE:
            hash_int = int(tmp_block.hash(), 16)	# 16进制
            if hash_int < self._target:	# 区块头的散列值满足要求,小于target,算法完成
                break
            else :	# 区块头的散列值不满足要求,nonce加1,继续循环
               tmp_block.nonce+=1
        return tmp_block

这里还可以加一个验证功能,在后续将挖到的矿持久化以及上传数据库后,该值可随时进行验证,这里简单写为:

def validate_block(self, block):
        hash_int = int(block.hash(), 16)
        return True if hash_int<self._target else False

有了工作量证明我们的 Blockchain 类,在生成新区块的时候就都需要通过 proofOfWork 类的 mine_block 方法来生成区块了,另外为了更易于理解我们相应的将 Blockchain 类的 add_block 方法也更名为 mine_block 方法,在 print_chain 方法中我们加入了对区块的工作量证明校验结果,因此修改后Blockchain 类的完整代码如下:

from block import Block
from datetime import datetime
from proofofwork import ProofOfWork


class BlockChain(object):
    def __init__(self):
        print('Created a new blockchain.\n')
        self.chain = []
        self.pow = ProofOfWork()  # 创建工作量证明类
        genesis_block = self.pow.mine_block('This is a genesis block') # 通过工作量证明类来挖出创世区块
        self.chain.append(genesis_block)

    # 挖出一个数据为data的区块
    def mine_block(self, data):
        new_block = self.pow.mine_block(data, self.chain[-1].hash())
        self.chain.append(new_block)

    def print_chain(self):
        for block in self.chain:
            print('Block Hash: {}'.format(block.hash()))
            print('Prev Block Hash: {}'.format(block.prev_block_hash))
            print('TimeStamp: {}'.format(datetime.fromtimestamp(block.timestamp)))
            print('Nonce: {}'.format(block.nonce)) # 增加打印 nonce值
            print('Data: {}'.format(block.data))
            print('POW: {}'.format(self.pow.validate_block(block))) # 校验区块
            print('')

我们更改main.py运行为:

from block import Block
from blockchain import BlockChain


if __name__ == '__main__':
    # 创建区块链
    block_chain = BlockChain()

    # 挖出第一个区块
    block_chain.mine_block('Send 1 BTC to Ivan')
    # 挖出第二个区块
    block_chain.mine_block('Send 2 more BTC to Ivan')

    # 打印整个区块链的信息
    block_chain.print_chain()

在这里插入图片描述

这里以每4位为首位添一位0,因为默认的difficulty_bits为12,可以看到截图中,有3位的16进制0开头,换算为12位的二进制0,而每向上添加4位,都将增加其计算量,比如说将其改为24,增大两倍,我们使用Linux默认计算时间工具测试:

time python3 main.py

在这里插入图片描述
很明显,对比上一个,时间慢了一大截,因为现在还是单线程,看单核情况发现,直接将我这个学生机的CPU的1核跑满了:

在这里插入图片描述
所以,这也是为什么在比特币大火的20年到22年间,各种云服务器被植入挖矿程序,导致每次上去查看,要不就显卡跑满,要不就CPU整个也全部跑满,原因就是太密集的哈希计算,让日常使用都不会触顶的服务器,第一次有了算不过来的感觉,emmm。。。

当然,之后的交易以及网络等篇幅还会对工作量算法进行修改,比如说预防作弊等,具体的可以见如下图,但也不会做得那么完全,那么至此,本篇结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/165277.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

制造企业数据/经营分析框架

背景 随着大数据技术发展以及数字化转型概念的普及&#xff0c;传统企业特别是制造业&#xff0c;也开始投入人力、资金&#xff0c;建立自己的数据分析团队&#xff0c;期望通过数据分析赋能企业的发展/转型。尽管&#xff0c;国内华为、美的、海尔、三一、徐工等制造业龙头企…

挡不住,逃不过,还是阳了

自从放开后&#xff0c;身边的&#x1f40f;陆陆续续多了起来。现在都不敢在食堂吃饭了&#xff0c;每次都是打包到工位吃&#xff0c;上班时也是都戴着口罩。每天回家后都是一顿扫射&#xff0c;用医用酒精做全身消毒。但是&#xff0c;还是没挡住&#xff0c;该来的还是来了。…

Leetcode:450. 删除二叉搜索树中的节点(C++)

目录 问题描述&#xff1a; 实现代码与解析&#xff1a; 递归&#xff1a; 原理思路&#xff1a; 含有内存释放版&#xff1a; 问题描述&#xff1a; 给定一个二叉搜索树的根节点 root 和一个值 key&#xff0c;删除二叉搜索树中的 key 对应的节点&#xff0c;并保证二叉搜…

Day860.高性能数据库连接池HiKariCP -Java 并发编程实战

高性能数据库连接池HiKariCP Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于高性能数据库连接池HiKariCP的内容。 实际工作中&#xff0c;总会难免和数据库打交道&#xff1b; 只要和数据库打交道&#xff0c;就免不了使用数据库连接池。 业界知名的数据库连接池…

2023/1/15 JS-作用域与作用域链

1 作用域 理解 - 就是一块"地盘", 一个代码段所在的区域&#xff0c;它是静态的(相对于上下文对象), 在编写代码时就确定了 分类&#xff1a; 全局作用域函数作用域 没有块作用域(ES6有了) -->(java语言也有) if (true) {var a 3}console.log(a); // 3作用&am…

用互联网思维做产品,做超出用户预期的产品

做出超出用户预期的产品&#xff0c;做出让用户惊喜和兴奋的产品。超越用户期望极致体验极致服务极致产品(极致产品功能情感温度)关心、关注、尊重用户理解用户理解人性用户参与。只有深刻了解&#xff0c;深刻理解用户&#xff0c;深刻理解人性&#xff0c;才能做出好的产品。…

Maven安装教程讲解

目录一、下载安装JDK二、下载 Maven三、配置 Maven 环境变量四、配置 Maven 仓库地址五、配置 Maven 镜像六、配置 Maven JDK七、IDE配置 Maven八、IDE新建 Maven 项目九、IDE执行 Maven 命令一、下载安装JDK 教程&#xff1a;https://blog.csdn.net/weixin_43888891/article/…

【阶段四】Python深度学习09篇:深度学习项目实战:循环神经网络处理时序数据项目实战:CNN和RNN组合模型

本篇的思维导图: 项目背景 时间序列数据集中的所有数据都伴随着一个时戳,比如股票、天气数据。这个数据集,是科学家们多年间用开普勒天文望远镜观察并记录下来的银河系中的一些恒星的亮度。广袤的宇宙,浩瀚的星空在过去很长一段时间里,人类是没有办法证明系外行星…

关于一次python服务性能的问题分析定位

今天项目遇到一个比较棘手的问题&#xff0c;我们做的接口平台&#xff0c;提供了一个给用户自己编辑关键字的逻辑&#xff0c;发现对应服务的cpu基本都满了&#xff0c;并且通过扩容的方式也没有好转&#xff0c;也同样被打满。 找了测试的同学了解了下&#xff0c;发现他们使…

Enterprise JavaBean 简介

Enterprise JavaBean 简介 在顶层我们使用的框架是面向服务的&#xff0c;而在其之下的开发方法面向组件&#xff0c;最基层的便是面向对象面向过程的JAVA编程。 面向过程和面向对象是两种具体程序设计时的编程范式&#xff0c;他们没有本质区别&#xff0c;只是使用不同的方…

【Linux】Linux命令行git和Linux调试器-gdb的使用

文章目录一、Linux命令行git1. git是什么2. Gitee上创建仓库&#xff0c;克隆仓库到本地3. git三板斧二、Linux调试器-gdb的使用1. debug和release的区别2. 常用命令一、Linux命令行git 1. git是什么 git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很…

分享52个Java源码,总有一款适合您

Java源码 分享52个Java源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c;大家下载后可以看到。 源码下载链接&#xff1a;https://pan.baidu.com/s/1YpNL5QWwQ18Y-KRmFHFs5g?pwdqc8w …

吴恩达机器学习课程笔记:监督学习、无监督学习

1.吴恩达机器学习课程笔记&#xff1a;监督学习、无监督学习 吴恩达机器学习系列课程&#xff1a;监督学习 吴恩达机器学习系列课程&#xff1a;无监督学习 仅作为个人学习笔记&#xff0c;若各位大佬发现错误请指正 机器学习的学习算法&#xff1a;监督学习、无监督学习、半…

1591_AURIX_TC275_PMU_Flash的保护

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 可以支持保护功能的Flash是前面文档中呼出来的几块&#xff0c;其中DFlash由于多模块公用&#xff0c;还有具体的拆分。PFlash可以进行两方面的保护&#xff0c;一个是错误的读取方式保护&…

python列表(list)底层实现

list Python内存管理中的基石 Python中所有类型创建对象时&#xff0c;底层都是与PyObject和PyVarObject结构体实现&#xff0c;一般情况下由单个元素组成对象内部会使用PyObject结构体&#xff08;float&#xff09;、由多个元素组成的对象内部会使用PyVarObject结构体 2个…

应用层——电子邮件(SMTP、POP3、IMAP)

目录 1. 电子邮件系统及组成结构 1.1 电子邮件 1.2 电子邮件系统的组件 2. SMTP(邮件发送协议) 2.1 SMTP的特征 2.2 SMTP的基本操作 2.3 SMTP协议的基本流程 2.4 SMTP交互与应答 2.5 SMTP与HTTP的对比 3. 邮件报文格式与MIME 3.1 电子邮件格式 3.2 多用途网际…

STM32时钟系统原理和作用

时钟对于单片机来讲类似于心脏对于人体&#xff0c;单片机通过时钟信号控制单片机工作。 1.时钟系统工作流程 时钟源输入----被分频或倍频----输出至系统/外设/外部接口 2.时钟分类 RC时钟精度低&#xff0c;体积小&#xff0c; OSC时钟精度高&#xff0c;体积大。 3.部分…

5.多层感知机

5.多层感知机 目录 感知机 基本内容训练感知机感知机存在的问题总结 多层感知机 隐藏层 单隐藏层-单分类 激活函数 ReLU函数sigmoid函数tanh函数总结 多类分类 多层感知机的从零开始实现 初始化模型参数激活函数模型损失函数训练 多层感知机的简洁实现 模型模型选择、欠拟合和…

⭐基于unity物体移动的方法API的总结-GIF图文详解⭐

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏 unity常用API ⭐相关文章&#xff1a; 线性差值函数以及平滑阻尼的运用和实践&#xff08;Lerp AND SmoothDa…

文献阅读:Towards Out-of-Distribution Sequential Event Prediction:A Causal Treatment

文献阅读&#xff1a;Towards Out-of-Distribution Sequential Event Prediction&#xff1a;A Causal Treatment 1. 问题描述2. 方法 & 模型设计 1. loss定义2. 模型设计 3. 实验 & 结果 1. 基础实验2. CaseQ策略细节分析 4. 结论 & 总结 文献链接&#xff1a;ht…