【区块链】用Python实现一条区块链

news2024/11/16 17:27:33

用Python实现一条区块链

点击链接获取可执行文件

本文使用 python 实现了一条简单的区块链。主要分两个模块:实现每个区块的结构和相关的方法、实现整条区块链的结构和相关的方法。下面是对这两个模块的描述。

每个区块主要包括两个成员:区块头和区块体。其中,区块头存储和区块本身相关的一些信息,例如索引值、时间戳、前驱区块的哈希值、本区块的哈希值等;区块体存储交易序列,用一个 list 结构来表示。区块中的方法主要包括两个:根据交易序列获取哈希值、结构化打印本区块的信息。

区块的整体结构如下所示:

在这里插入图片描述

# 实现每个区块的结构和相关的方法
import json
from datetime import datetime


# 区块链中每一个block的结构
class Block():
    def __init__(self, index=0, timestamp=0, previous_hash=0, 
                 merkle_root=0, nonce=0, transactions=None):
        self.block_header = {'index': index, 
                             'timestamp': datetime.timestamp(datetime.now()), 
                             'previous_hash': previous_hash,
                             'merkle root': merkle_root, 'nonce': nonce}
        self.transactions = transactions
        
        # 根据交易信息来计算merkle_root
        self.block_header['merkle root'] = self.get_merkle_root().hash_value
    
    
    # merkle tree中的结点
    class MerkleNode:
        def __init__(self, node_value, node_id):
            self.left = None
            self.right = None
            self.node_value = node_value
            self.node_id = node_id
            self.hash_value = hashlib.sha256(str(node_value).encode('utf-8')).hexdigest()

    # 获取merkle tree根节点的哈希值
    def get_merkle_root(self):
        node_id = 0
        nodes = []
        for transaction in self.transactions:
            nodes.append(self.MerkleNode(transaction, node_id))
            node_id += 1
        while len(nodes) != 1:
            # 为当前层的结点创建父结点
            parent_nodes = []
            # 为每一对node生成一个parent
            for i in range(0, len(nodes), 2):
                left_node = nodes[i]
                if i + 1 < len(nodes):
                    right_node = nodes[i + 1]
                else:
                    parent_nodes.append(nodes[i])
                    break
                combined_hash_value = left_node.hash_value + right_node.hash_value
                parent_node = self.MerkleNode(combined_hash_value, node_id)
                node_id += 1
                parent_node.left = left_node
                parent_node.right = right_node
                parent_nodes.append(parent_node)
            # 进行下一层结点哈希值的计算
            nodes = parent_nodes
        return nodes[0]
    
    # 打印本区块的信息
    def print_block(self):
        print_format = {'block header': self.block_header, 
                        'transactions': self.transactions}
        print(json.dumps(print_format, indent=4))

整条区块链主要包括五个成员变量,分别用于定义各区块的容量、存储总的交易序列、存储每一个区块的数据结构、存储新构建的当前区块、挖矿难度。

创世区块产生的逻辑为:当用户初始化一条区块链后,此时区块链中的数据变量也初始化了,但还没有一个实例化的区块。随着交易的累积,到达产生新块的阈值后,区块链会构造出它的第一个新区块,称为创世区块。相比于别的区块,它的特殊点仅仅在于nonce初始值为10,且不需要工作量证明操作,而后续区块的nonce值都根据前驱区块的nonce值计算出来。

新区块产生的逻辑为:每当交易池达到阈值后,分布式网络上的矿工会根据前驱区块的nonce值来不断寻找一个符合要求的数值。当成功找到该值后,该矿工会可以获得构造新块的权力,并将该值作为nonce存储在当前区块中。

工作量证明算法的逻辑为:记前驱区块的nonce值为p,矿工随机选取一个数值p’(本实验中假设该值均为0),循环计算target=hash(p*p’),直到target[0:k]都是0为止(k是设置的挖矿难度)。当成功找到该值后,矿工获得构造新块的权力,并将该值作为nonce存储到新块中。

# 实现整条区块链的结构和相关的方法
class Blockchain(object):
    def __init__(self, block_transaction_capacity, difficulty):
        """
        创建交易池、本地区块链并添加创世区块
        """
        # 每个区块最多储存block_transaction_capacity笔交易
        self.block_transaction_capacity = block_transaction_capacity
        self.transaction_pool = []
        self.chain = []
        # TODO: 创建创世区块并加入区块链
        self.current_block = None
        # 挖矿难度
        self.difficulty = difficulty

    def add_block(self, proof, previous_hash=None):
        """
        TODO: 创建一个新的区块到区块链中,并返回创建成功的区块
        :param proof: <int>                    由工作证明算法生成的证明
        :param previous_hash: <str>            前一个区块的 hash 值
        :return: <dict>                        新区块
        """
        self.current_block = Block(nonce=proof, transactions=self.transaction_pool
                                   [-self.block_transaction_capacity:], previous_hash=previous_hash, 
                                   index=len(self.chain))
        self.chain.append(self.current_block)


    def add_transaction(self, sender, receiver, amount):
        """
        TODO: 添加一笔新的交易到交易池中
        :param sender: <str>   发送者
        :param receiver: <str> 接收者
        :param amount: <int>   金额
        :return: <bool>        是否添加成功
        """
        transaction = str(sender) + ' ' + str(receiver) + ' ' + str(amount)
        self.transaction_pool.append(transaction)
        
        pool_size = len(self.transaction_pool)

        # 交易池就绪,应该创建新区块
        if pool_size % self.block_transaction_capacity == 0:
            # 先处理创世块
            if len(self.chain) == 0:
                self.current_block = Block(nonce=100, transactions=self.transaction_pool
                                           [-self.block_transaction_capacity:])
                self.chain.append(self.current_block)
            else:
                # 获取当前块的哈希值
                current_block_hash = self.__last_block()
                # 执行计算任务,并获取工作量证明
                pre_proof = self.current_block.block_header['nonce']
                proof = self.proof_of_work(pre_proof)
                # 计算完成,添加区块
                self.add_block(proof, current_block_hash)
            
        return True


    def __last_block(self):
        """
        TODO: 返回本地区块链(最长链)上的最后一个区块的 hash 值
        return: <str>
        """
        return self.current_block.block_header['merkle root']


    def __hash(block):
        """
        给一个区块生成 SHA-256 值
        :param block: <dict>
        :return: <str>
        """
        block_string = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()


    def proof_of_work(self, pre_proof):
        """
        TODO: 简易工作量证明算法
         - 找到一个数字p,使得p*p'的hash值的前2个字符是0
         - 其中p'是前一个区块的proof,找到的p是工作量证明
        :param pre_proof: <int>
        :return: <int>
        """
        p = 0
        target = '1' * 100
        
        while not target[0:difficulty] == '0' * self.difficulty:
            target = hashlib.sha256(str(p * pre_proof).encode('utf-8')).hexdigest()
            p += 1
        return p


    def print(self):
        """
        TODO: 打印区块链
        """
        print('\033[1;31m', 
              len(self.chain), 'blocks and', len(self.transaction_pool),
              'transactions in this private chain:', 
              ':\033[0m')
        idx = 0
        for block in self.chain:
            print('\033[1;31m', 'Block', idx, ':\033[0m')
            idx += 1
            block.print_block()
        pending_num = len(self.transaction_pool) % self.block_transaction_capacity
        if pending_num == 0:
            print('\033[1;31m',
                  '0 pending transactions in this private chain.'
                  '\033[0m')
        else:
            print('\033[1;31m',
                  pending_num, ' pending transactions in this private chain:'
                  '\033[0m')
            print(self.transaction_pool[-pending_num:])

下面是测试部分。首先定义好区块链的结构后,随机生成交易双方的用户名和交易额,不断将这些交易信息输入到区块链中,最后打印整条区块链的结构。

import random


# 定义每个块所能存储的交易数
block_transaction_capacity = 4
# 定义总的交易数量
num_all_transactions = 9
# 定义挖矿难度(为k表示要求哈希前k位为0)
difficulty = 5
# 初始化本地区块链
blockchain = Blockchain(block_transaction_capacity, difficulty)

seed = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX0123456789'

def generate_transactions(num_all_transactions):
    for i in range(num_all_transactions):
        random_sender, random_receiver = [], []
        for i in range(6):
            random_sender.append(random.choice(seed))
            random_receiver.append(random.choice(seed))
        random_sender = ''.join(random_sender)
        random_receiver = ''.join(random_receiver)
        yield random_sender, random_receiver, random.uniform(0, 100000)


for sender, receiver, amount in generate_transactions(num_all_transactions):
    blockchain.add_transaction(sender, receiver, amount)

# 打印区块链结构
blockchain.print()

程序运行结果为:

在这里插入图片描述

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

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

相关文章

GlobalWebsoket.js 的使用,实现获取实时数据

在称重小程序是使用 GlobalWebsoket 实现获取实时数据前言一、逻辑分析二、实现方式1.方法整体流转分析 -- 初始化并绑定1. onLoad1. init2. getDeviceInfo3. initWebSocket4. setProperties2.方法整体流转分析 -- 解除绑定1. onBackPress2. remoeSubscribe三、参数调用分析四、…

“大数据分析”相比“传统数据分析”优势明显,体现在哪些方面

一、大数据和数据分析的定义&#xff1a; 数据分析&#xff1a;指使用适当的统计分析方法来收集数据&#xff0c;以进行大量数据分析。 大数据分析&#xff1a;指在可承受的时间范围内无法使用常规软件工具捕获&#xff0c;管理和处理的数据集合&#xff1b; 数据分析的核心…

【web渗透思路】任意账号的注册、登录、重置、查看

目录 一、任意用户注册 1.未验证邮箱/手机号 2、不安全验证邮箱/手机号 3.批量注册 4.个人信息伪造 5.前端验证审核绕过 6.用户名覆盖 二、任意用户登录 1、万能密码 2、验证码、密码回显 3、登录检测不安全 三、任意账号重置 1、重置账号名 2、验证码 3、MVC数…

2022年第三季度泛出行行业洞察:泛出行行业正在经历数智化升级的关键时期,用户规模保持平稳增长,行业整体良性发展

易观分析&#xff1a;泛出行行业涵盖综合车主服务、车辆加油充电、网约车、旅游预定、酒店预定、户外出行等领域。当前泛出行领域正在经历传统模式向数智化新模式的转变&#xff0c;智能化升级和服务品质提升在该领域变革中正发挥着积极的作用。未来泛出行领域将在数智化、电动…

Web3:价值投资的范式转移

​潜力博主推荐&#xff0c;点上面关注博主 ↑↑↑ 进化是宇宙中最强大的力量&#xff0c;是唯一永恒的东西&#xff0c;是一切的驱动力。———桥水基金 雷.达利奥 时间拉长&#xff0c;进化才是人类的主旋律。过去&#xff0c;环境的变化是进化的主因。 现在&#xff0c;技…

Servlet | 深度剖析转发和重定向

一&#xff1a;深度剖析转发和重定向 &#xff08;1&#xff09;在一个web应用中通过两种方式可以完成资源的跳转 第一种方式&#xff1a;转发方式 第二种方式&#xff1a;重定向方式 &#xff08;2&#xff09;转发和重定向的区别 区别一&#xff1a;代码上的区别 ①转发 &a…

阿里资深专家撰写出的 Nginx 底层与源码分析手册,GitHub 已爆赞

NGINX 发展史&#xff1a; 过去最主流的服务器是 1995 年发布的 Apache 1.0。Apache 源于 NCSAHTTPd 服务器&#xff0c;是一个多进程模型的 Web 服务器。但运行到后期&#xff0c;Apache 渐渐出现很多问题&#xff0c;比如内存占用很大、扩展需挂接第三方库、并发能力受限等。…

高效的股票数据接口工具有哪些?

我们已经知道了量化投资是是通过数量化方式及计算机程序化发出买卖指令&#xff0c;以获取稳定收益为目的的交易方式&#xff0c;而其中最重要的载体是数据。在金融领域中量化的应用让金融分析师、外汇交易员、产品研发员等技术人员又有了新的用武之地&#xff0c;转型成为量化…

【微信小程序】saveFile:fail tempFilePath file not exist

开发微信小程序尝试保存文件时&#xff0c;会提示saveFile:fail tempFilePath file not exist错误&#xff0c;是什么问题呢&#xff0c;接下来带你如何分析和解决问题 文章目录1. 定位问题2. 解决问题1. 定位问题 首先&#xff0c;看一下代码怎么写得&#xff0c;如下所示 w…

数据结构之线性表中的顺序表【详解】

前言 现在是北京时间11月24号0点2分&#xff0c;天气有一些冷&#xff0c;我现在很困&#xff0c;但是博客还没写&#xff0c;我很想睡觉&#xff0c;假如我现在放弃的码字&#xff0c;往床上一趟&#xff0c;其实也不怎么样&#xff0c;但是我们不能有拖延症&#xff0c;所以…

关于元宇宙的六七八你知道多少?

&#x1f3e0;个人主页&#xff1a;黑洞晓威 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晓威&#xff0c;一名普普通通的大二在校生&#xff0c;希望在CSDN中与大家一起成长。&#x1f381;如果你也在正在学习Java&#xff0c;欢迎各位大佬来到我的博客查漏补缺…

【MySQL篇】第三篇——表的操作

创建表 创建表案例 查看表结构 修改表 删除表 创建表 在创建数据库之后&#xff0c;接下来就要在数据库中创建数据表了。所谓创建数据表&#xff0c;指的是在已经创建数据库中建立新表。 创建数据表的过程是规定数据列的属性的过程&#xff0c;同时也是实施数据完整性&am…

JAVA实训第二天

目录 JDK8新特性 Java8介绍 JAVA 8主要新特性 Java 8接口增强-默认方法 接口 接口的应用 Lambda表达式介绍 Lambda表达式的写法 功能性接口Lambda表达式的应用 函数接口 JDK8新特性 Java8介绍 •Java8是Java发布以来改动最大的一个版本&#xff0c;其中主要添加了函数式…

自定义数据类型——结构体

我们今天来简单介绍一下结构体。 目录 1. 结构体的声明 2. 结构体成员的访问 3. 结构体传参 首先我们要知道为什么会有结构体的存在&#xff0c;我们的生活里有很多东西&#xff0c;比如一只猫&#xff0c;一本书&#xff0c;一个人&#xff0c;我们如果要用程序来描述他们…

C语言 指针

C语言 指针引言1. 什么是指针2. 简单认识指针3. 取地址符 & 和解引用 * 符一、指针与内存二、指针类型的存在意义1. 指针变量的大小2. 指针移动3. 不同指针类型的解引用三、指针运算1. 指针加减整数程序清单1程序清单22. 指针 - 指针3. 指针关系运算四、二级指针五、野指针…

这个双11,我薅了华为云会议的羊毛

文章目录前言华为云会议悄然助力各行各业和其他云会议产品相比&#xff0c;华为云会议优势是什么&#xff1f;云端一体线下会议室和云会议互通专业会管与会控能力更安全华为云会议有哪些 AI 能力&#xff1f;华为云会议入门有多简单&#xff1f;下载步骤如下安装加入会议预约会…

原生js 之 (DOM操作)

Web API Web API是浏览器提供的一套操作浏览器功能和页面元素的API(BOM和DOM) JavaScipt由三部分构成 ECMAScript、DOM和BOM。 BOM浏览器对象模型&#xff0c;提供了与浏览器的交互方法和接口&#xff1b; DOM 文档对象模型&#xff0c;提供了处理网页内容、结构 、样式的方法…

【数据结构】图的遍历

深度优先遍历 深度优先遍历思想 对于图&#xff1a;选中一个结点&#xff0c;访问和其相邻的、未被访问过的点&#xff0c;全部访问完毕后回退到上一个结点&#xff0c;直至全部结点访问完毕&#xff0c;类似于图的先序遍历&#xff0c;如有邻接表&#xff0c;则按邻接矩阵的顺…

一文熟悉 Go 的基础语法和基本数据类型

一文熟悉 Go 的基础语法和基本数据类型前言Hello&#xff0c;World&#xff01;有关 main 函数的一些要点关键字package声明引入基本数据类型整形数据类型有符号整数类型无符号整数类型其他整数类型浮点数据类型字符类型布尔类型字符串类型基本数据类型的默认值常量和变量声明结…

swift指针内存管理-指针类型使用

为什么说指针不安全 我们在创建一个对象的时候&#xff0c;是需要在堆上开辟内存空间的 但是这个内存空间的声明周期是有限的 也就意味着如果使用指针指向这块内存空间&#xff0c;当这块内存空间的生命周期结束&#xff08;引用计数为0&#xff09;&#xff0c;那么当前的指针…