一 . 数据格式与Linux命令
1.crul命令
-
Curl 是一个命令行工具。curl能访问远程服务器并从远程服务器下载数据,也可以向远程服务器发送数据。
-
curl工具在centos操作系统下的安装
-
#yum install curl
-
在MobaXterm中用curl访问百度首页
-
#curl www.baidu.com
-
curl命令的格式为:
-
curl [option] [url]
-
curl 命令常见option用法
-
选项 描述 -A/--user-agent 设置用户代理发送给服务器 -C/--continue-at 设置为断点续传 -X 设置请求的方法 -T/--upload-file 设置为上传文件模式 -u/--user 设置访问服务器的用户和密码
2.tree命令
-
tree工具在centos操作系统下的安装
-
sudo yum install tree
-
使用tree命令将返回树形文件目录格式,常用tree命令使用格式为:
-
tree [option] [参数]
-
tree命令常用的选项愈描述
-
选项 描述 -a 显示所有文件和模式 -L 显示层级变量 -d 显示目录名称 -D 列出文件或目录的更改时间 -p 列出权限 -s 列出文件或目录的大小 -t 按文件更改时间先后排序
3.jq命令
-
jq命令功能是允许直接在命令行下对JSON文件进行操作,包括分片、过滤、转换等,
-
jq的命令格式:
-
#jq [options] filter [files]
-
Linus系统中没有预装jq命令,以Centos为例,
-
yum install -y eqel -release yum install -y jq
-
将jq命令通过管道与其他命令联用
-
echo '{"foo": 0 }' | jq .
4.python Web服务器(Flask)
-
安装Flask框架
-
pip install Flask
-
from flask import Flask # 创建一个 Flask 应用程序实例 app = Flask(__name__) # 定义路由和视图函数 @app.route('/') def hello(): return 'Hello, world!' # 运行 Flask 应用程序 if __name__ == '__main__': app.run()
-
二 . 密码学基础
1 . 哈希函数
1) . 哈希函数的概念
哈希函数是一种计算机算法,将任意大小的数据映射成固定长度的散列值。哈希函数具有以下特性:
-
输入数据的长度可以任意,但输出的散列值长度是固定的。
-
输入数据的任意小的变化都会导致输出散列值的巨大变化。即使输入数据只有一个字节不同,计算出的散列值也会完全不同。
-
散列值的计算过程是高效的,即使输入数据很大,计算散列值的时间也应该是相对较短的。
-
从散列值无法确定原始输入数据。通过散列值无法推导出原始数据的内容。
2) . 哈希函数的特性
-
一致性:如果相同的输入被作为哈希函数的输入,它们将始终产生相同的散列值。即使在不同的程序、不同的计算机上计算,结果也应该是一致的。
-
均匀性:哈希函数应尽可能将输入数据的每一位都平均地映射到散列值的每一位上,避免出现冲突。
-
不可逆性:无法通过散列值推导出原始的输入数据。即使输入数据仅发生微小变化,也会导致输出散列值的巨大变化。
-
高效性:计算散列值的过程应该是高效的,无论输入数据的大小,计算时间应该相对较短。
-
差异性:输入的微小变化会导致输出散列值的明显差异。哈希函数应该有良好的散列性能,尽可能避免冲突,使得不同的输入有不同的散列值。
-
抗碰撞性:哈希函数应该具有较高的抗碰撞能力,即不同的输入尽可能产生不同的散列值。理想情况下,哈希函数是碰撞无关的,即找到任意两个不同的输入具有相同的散列值的概率极低。
3). 算法的应用
使用python语言实现哈希算法的加密。Python3中已植入了hashlib依赖包,如下代码为使用hashlib的python代码实现:
#引用hashlib依赖包
import hashlib
#定义需要加密的变量
words = 'Hello World'
#使用sha256算法加密
hash_code = hashlib.sha256(words.encode()).hexdigest()
#打印加密后的结果
print(hash_code)
2.非对称加密算法
1). 对称加密
对称加密是一种加密算法,使用相同的密钥(也称为密钥或秘密密钥)对数据进行加密和解密。在对称加密中,发送方使用密钥将明文消息转换为加密的密文,然后将密文发送给接收方。接收方收到密文后,使用相同的密钥将其解密为原始的明文消息。
对称加密算法有很多种,其中一些常见的算法包括:
-
DES(Data Encryption Standard):是一种使用56位密钥的对称加密算法,已经不安全,被更先进的算法所取代。
-
3DES(Triple Data Encryption Standard):是DES算法的加强版,使用3个56位密钥进行多轮加密,提供更高的安全性。
-
AES(Advanced Encryption Standard):是目前最广泛使用的对称加密算法,支持不同的密钥长度(如128位、192位、256位)。
-
Blowfish:是一种快速的对称加密算法,支持可变长度的密钥(32位至448位)。
-
RC4(Rivest Cipher 4):是一种流密码算法,通常用于加密通信协议(如SSL和WEP)。
对称加密算法具有以下优点:
-
加密和解密过程速度快,适用于大数据量的加密操作。
-
实现简单,适用于各类硬件平台。
-
通常提供较高的加密速度和性能。
2). 非对称加密
非对称加密是一种加密算法,使用两个相关联的密钥,分别称为公钥和私钥。公钥是公开的,可以被任何人获得和使用,而私钥是保密的,只有密钥的拥有者可以使用。在非对称加密中,使用公钥加密的数据只能用相应的私钥解密,而使用私钥加密的数据只能用相应的公钥解密。
非对称加密算法有多种,其中一些常见的算法包括:
-
RSA(Rivest-Shamir-Adleman):是一种基于数论的非对称加密算法,广泛用于加密通信和数字签名。
-
ECC(Elliptic Curve Cryptography):是一种基于椭圆曲线数学问题的非对称加密算法,具有相比于RSA更短的密钥长度和更高的加密性能。
-
DSA(Digital Signature Algorithm):是一种只用于数字签名的非对称加密算法,用于验证消息的完整性和身份认证。
非对称加密算法具有以下优点:
-
公钥可以公开分发,私钥保密存储,方便密钥的管理和分发。(公钥加密,私钥验签。)
-
具有较高的安全性,私钥保持机密,因此即使公钥被攻击者获取,也无法解密数据。(私钥验签,公钥解密)
然而,与对称加密算法相比,非对称加密算法通常更加复杂和耗时。因此,在实际应用中,常常将对称加密算法与非对称加密算法结合使用,以兼顾安全性和性能。例如,使用非对称加密算法对对称密钥进行加密和传输,然后使用对称密钥进行实际的数据加密和解密,这种方式被称为混合加密。这样既能保证密钥的安全传输,又能获得更高的加密和解密效率。
3). 算法的应用
使用python语言实现椭圆曲线算法(ECC)的签名与验证
-
安装ecdsa依赖
-
pip install ecdsa
-
使用ecdsa进行签名与验证
-
这里定义两个函数分别是sign(签名)和verify(验证)
-
sign函数的输入参数和返回参数
-
输入参数 data:签名使用的数据 private_key:签名使用的私钥 返回参数 sig:(签名内容)
2.创建Crypto_util.py文件,在其中加入以下内容实现sign函数:
import ecdsa
import random
import hashlib
import os
def sign(data, private_key):
"""
签名
:param data: 签名时使用的数据
:param private_key: 签名时使用的私钥
:return:
"""
sk = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.SECP256k1)
sig = sk.sign(data)
return sig
-
verify函数的输入参数及返回参数
-
输入函数 data:验证使用的数据 sig:验证使用的签名 public_key:公钥 返回参数 0 :验证成功 1:验证失败 2:验证失败并报错
3.在crypto_util.py文件中继续添加verify函数内容:
-
def verify(data, sig, public_key): """ 验证签名 :param data: 验证时使用的数据 :param sig: 要验证的签名 :param public_key: 验证时使用的公钥 :return: 验证结果,返回0表示成功,返回1表示失败,返回2表示出现问题 """ vk = ecdsa.VerifyingKey.from_pem(public_key) try: if vk.verify(sig, data): return 0 # 如果验证成功则返回0 else: return 1 # 如果验证失败则返回1 except Exception as e: return 2 # 如果验证出现问题则返回2
-
4.可以在crypto_util.py中使用sign和verify函数实现签名与验签
import random # 1.创建签名的原始数据 string = 'hello world' # 2.生成私钥 private_key = ''.join(random.sample ('abcdefghijklmnopqrstuvwxyz!@#$%^&*()', k=16)).encode() print(f'私钥是:{private_key}') #3.根据私钥生成公钥 public_key = ecdsa.SigningKey.from_string (private_key, curve=ecdsa.SECP256k1) .verifying_key.to_pem() print(f'公钥是:{public_key}') #4.签名 sig= sign(string.encode(), private_key_bytes) # 5.验证签名 print(verify(string.encode(),sig,public_key))
-
3. 实战一.构建具备加密功能的Flask服务端
1.练习目标:
构建基于Flask框架的Web服务端,并在服务端中实现哈希算法加密功能。
2.任务内容:
(1)架构Flask Web服务端,实现GET与POST请求
(2)在Web服务端中加入哈希加密算法功能。
3.具体实现:
步骤一: 构建哈希算法的处理函数
在2.3.2节创建的simple app 中加入services.py文件,并在其中加入哈希算法加密功能,具体代码内容如下:
import hashlib
def hash_encrypt(input):
"""
使用哈希算法加密
:param input:客户端发送的数据
:return:返回给客户端的加密数据
"""
hash_code = hashlib.sha256(input.encode()).hexdigest()
return hash_code
三 . 区块链的区块与账本
1.学习目标:
-
掌握区块链中区块链的基本概念,能够说明区块中的数据内容
-
掌握区块链中区块与链的联系,能够说明区块链的具体实现形式
-
掌握区块链中账本的概念,能够使用程序设计编码形式实现账本功能。
-
掌握通过程序编码实现区块链中各实体的方式,能够实现简单的区块链系统
2.账本的具体实现
将基于账本的基本概念以代码的形式实现。首先需要创建两个对象分别block和Blockchain
-
Block对象:代表区块的概念,对象中包括区块索引、上一区块的哈希值、区块体数据,区块时间戳以及区块自身哈希值。
-
Blockchain对象:代表一个简易版的区块链,其中主要包括了以区块和链概念组成的数据存储方式,并且在区块链创建时需要在其中加入创世区块
-
如下代码为Block对象和Block的python代码实现,需注意的区块自身哈希的生成方式将借助哈希算法实现,代码中定义了calc_block_hash方法将区块头中的信息进行整合后以哈希算法加密以区块对应的哈希值。
-
block和blockchain定义,实现区块和区块链的定义(models.py),并创建test.py验证
import json import hashlib from datetime import datetime INITIAL_BITS = 0x1e777777 #区块对象 class Block(object): def __init__(self, index, prev_hash, data, timestamp, bits): """ 区块的初始化方法,在创建一个区块需传入包括索引号等相关信息 :param index: 区块索引号 :param prev_hash: 前一区块的哈希值 :param data: 区块中需保存的记录 :param timestamp: 区块生成的时间戳 :param bits: 区块需传入的比特值(预留) """ self.index = index self.prev_hash = prev_hash self.data = data self.timestamp = timestamp self.bits = bits self.nonce = 0 self.elapsed_time = "" self.block_hash = self.calc_block_hash() def to_json(self): """ 将区块内容以JSON的形式输出 :return: """ return { "index": self.index, "prev_hash": self.prev_hash, "data": self.data, "timestamp": self.timestamp.strftime('%Y/%m/%d %H:%M:%S'), "bits": hex(self.bits)[2:].rjust(8,"0"), 'nonce': hex(self.nonce)[2:].rjust(8,"0"), 'block_hash': self.block_hash } def calc_block_hash(self): """ 生成区块对应的哈希值 :return: """ blockheader = str(self.index) + str(self.prev_hash) \ + str(self.data) + str(self.timestamp) + \ hex(self.bits)[2:] + str(self.nonce) h = hashlib.sha256(blockheader.encode()).hexdigest() self.block_hash = h return h class Blockchain(object): def __init__(self): """ 初始化区块链对象,操作包括: 1、定义一个以chain命名的区块链数组 2、在链中加入创世区块(genesis block) """ self.chain = [] self.create_genesis_block() def add_block(self, block): """ 将新的区块加入区块链chain中,该方法将不被外界调用 :param block: 新加入的区块 :return: """ self.chain.append(block) def query_block_info(self, index=0): """ 通过索引值查询区块链chain中的区块信息 :param index: 查询区块的索引值 :return: """ return json.dumps(self.chain[index].to_json(), sort_keys=True, ensure_ascii=False, indent=2) def create_genesis_block(self): """ 创建创世区块,创世区块内容如下: index -> 设置为0,代表第一个区块 prev_hash ->设置为64个"O"作为默认参数 data ->存储一段字符串 :return: """ genesis_block = Block(0, "0" * 64, "这是第一个区块(创世区块)", datetime.now( ), INITIAL_BITS) self.add_block(genesis_block) def add_new_block(self, data) : """ 可供调用的方法,用于添加新的区块 :param data: :return: """ last_block = self.chain[-1] #通过last_block获取chain中的最新区块,从而获取相应的index和prev_hash # #用于创建新区块时使用 block = Block(last_block.index + 1, last_block.block_hash, data, datetime.now( ), last_block.bits) self.chain.append(block) return last_block.index + 1
-
创建test.py文件,将以上代码放入其中,并再次加入对应的验证代码
-
import models blockchain = models.Blockchain() #创建区块链 print(blockchain.query_block_info(0))#查询区块高度为e,创世区块信息 from time import sleep sleep(1)#休息1s blockchain.add_new_block("新增的区块")# 添加新区块,高度为1 print(blockchain.query_block_info(1))
2.构建简单的区块链账本系统
1).练习目标:
以Web服务的形式持久化运行区块链,并通过HTTP接口的形式实现对区块链的操作
2).任务内容:
-
构建区块链的区块和区块链对象
-
使用Flask等Web服务框架运行持久化的进程实现以下功能:
-
基于HTTP接口实现新区块的添加
-
基于HTTP接口通过传递区块索引查询区块链中的区块。
-
3).具体实现
-
步骤一:创建项目以及安装依赖
-
步骤二:创建区块和区块链对象
创建models.py,在其中加入区块和区块对象的实现
import json import hashlib from datetime import datetime INITIAL_BITS = 0x1e777777 #区块对象 class Block(object): def __init__(self, index, prev_hash, data, timestamp, bits): """ 区块的初始化方法,在创建一个区块需传入包括索引号等相关信息 :param index: 区块索引号 :param prev_hash: 前一区块的哈希值 :param data: 区块中需保存的记录 :param timestamp: 区块生成的时间戳 :param bits: 区块需传入的比特值(预留) """ self.index = index self.prev_hash = prev_hash self.data = data self.timestamp = timestamp self.bits = bits self.nonce = 0 self.elapsed_time = "" self.block_hash = self.calc_block_hash() def to_json(self): """ 将区块内容以JSON的形式输出 :return: """ return { "index": self.index, "prev_hash": self.prev_hash, "data": self.data, "timestamp": self.timestamp.strftime('%Y/%m/%d %H:%M:%S'), "bits": hex(self.bits)[2:].rjust(8,"0"), 'nonce': hex(self.nonce)[2:].rjust(8,"0"), 'block_hash': self.block_hash } def calc_block_hash(self): """ 生成区块对应的哈希值 :return: """ blockheader = str(self.index) + str(self.prev_hash) \ + str(self.data) + str(self.timestamp) + \ hex(self.bits)[2:] + str(self.nonce) h = hashlib.sha256(blockheader.encode()).hexdigest() self.block_hash = h return h class Blockchain(object): def __init__(self): """ 初始化区块链对象,操作包括: 1、定义一个以chain命名的区块链数组 2、在链中加入创世区块(genesis block) """ self.chain = [] self.create_genesis_block() def add_block(self, block): """ 将新的区块加入区块链chain中,该方法将不被外界调用 :param block: 新加入的区块 :return: """ self.chain.append(block) def query_block_info(self, index=0): """ 通过索引值查询区块链chain中的区块信息 :param index: 查询区块的索引值 :return: """ return json.dumps(self.chain[index].to_json(), sort_keys=True, ensure_ascii=False, indent=2) def create_genesis_block(self): """ 创建创世区块,创世区块内容如下: index -> 设置为0,代表第一个区块 prev_hash ->设置为64个"O"作为默认参数 data ->存储一段字符串 :return: """ genesis_block = Block(0, "0" * 64, "这是第一个区块(创世区块)", datetime.now( ), INITIAL_BITS) self.add_block(genesis_block) def add_new_block(self, data) : """ 可供调用的方法,用于添加新的区块 :param data: :return: """ last_block = self.chain[-1] #通过last_block获取chain中的最新区块,从而获取相应的index和prev_hash # #用于创建新区块时使用 block = Block(last_block.index + 1, last_block.block_hash, data, datetime.now( ), last_block.bits) self.chain.append(block) return last_block.index + 1
-
步骤三:编写Floask项目的HTTP接口,实现功能,在models.py同级目录创建app.py文件
-
import models from flask import Flask,request,jsonify app = Flask (__name__) app.config['JSON_AS_ASCII'] = False blockchain = models.Blockchain() @app.route('/add',methods = ['POST']) def add(): """ 添加新区块接口,接口形式为POST 请求时通过在Body中加入类似{"data":"..."}的形式作为新区块的数据内容 :return: """ body = request.json index =blockchain.add_new_block(body['data']) #数据返回内容,在data中加入新加入区块的索引 return jsonify({ 'code':2020, 'data':index }) @app.route('/query',methods=['GET']) def query(): """ 区块查询接口,通过HTTP GET方法接收查询区块的索引 请求示例如:http://127.0.0.1:5000/query?index=0 :return: """ index = int(request.args['index']) #数据返回内容,返回查询区块的完整信息 return jsonify({ 'code':200, 'data':blockchain.query_block_info(index) }) if __name__ == '__main__': app.run()
-
步骤四:验证功能正确性
使用Postman分别调用add和query接口,验证区块添加和查询的正确性。
-
验证区块添加接口
使用Postman调用http://127.0.0.1:5000/add路径,设置HTTP方法为POST并在Body中设置对应请求内容
-
验证区块查询接口
使用Postman调用http://127.0.0.1:5000/query?index=0,设置HTTP方法为GET,查询创世区块内容
-
-
四.区块链的账户和交易
1.本章导学
相对于传统中心化系统,区块链系统在用户身份标和数据存储具有独特的设计。在用户身份标识方面,区块链采用加密技术生成了特殊的“账户”信息。数据存储方面,区块链在“区块,的概念基础上,更进一步地, 对每条数据进行了独有的设计并以“交易”的方式表示。本章节将分别介绍账户与交易的基本概念,并在此基础上介绍通过程序代码的基本概念实现。最后本章节没有一项综合 性较高的实战练习,通过整合区块链的基础知识,模拟区块链运行过程。通过本章学习,可以有效提升对区块链去中心化数据存储的理解。
2.学习目标
-
1.掌握区块链账户中公私钥的概念,能够通过程序设计编码的形式生成公钥和私钥。
-
2.掌握区块链账户地址的概念,能够通过程序设计编码的形式生成账户地址。
-
3.掌握区块链交易的基本概念以及交易的具体过程。
-
4.掌握区块链中交易以及区块的验证方法,能够通过程序设计编码的形式实现。
学习导图
:
3.区块链中的账户
与传统中心化系统账户所包含的用户名和密码不同,去中心化系统中的一个账户中包含了三个要素,分别是:地址、公钥以及私钥
.
-
地址
账户地址一般为固定长度的随机字符串,字符串长度为2的指数次方例如32位或64位,用于唯一标识账户。由于区块链网络具有去中心化的特点,如果账户使用用户自定义的账户名很容易出现名称重复而无法被主动识别的问题。由于地址在区块链中是公开的,任何人都可以查看,为了保护对应账户的隐秘性,地址的生成方式将采用多种加密技术从而实现公开性和保密性这两个矛盾的条件
-
公钥和私钥
区块链账户一般通过非对称加密技术的公钥和私钥替代传统中心系统的密码。根据公私钥加密的特性,一方面数据在公钥加密后可以通过私钥解密,另一方面,数据通过私钥签名后可以通过公钥验签。在区块链中,账户的公钥一般会公布在区块链网络中供其他节点获取和使用,而账户的私钥会在本地保存不被其他区块链网络的其他参与者获取。利用非对称加密技术,可以在有效保护用户隐私的前提下实现信息交互,例如当有账户A需要发送信息给账户B,由于账户A和账户B均加入了区块链网络,账户A拥有账户B的公钥而账户B
拥有账户A的公钥。当账户A需要像账户B发送信息时,账户A可以通过自己的私钥对信息加上签名,再通过账户B的公钥对信息加密。当信息发送至账户B后,账户B可以用自己的私钥解密发送的数据,再用账户A的公钥签证数据中的签名。如图4-2为公私钥在区块链网络数据传输时的作用:
-
应用场景:账户A向账户B发送信息
-
使用账户B的公钥加密
-
使用账户A的私钥签名
-
发送数据
-
接收数据
-
使用账户B的私钥解密
-
使用账户A的公钥验签
-
4.地址生成的相关过程
下面是完整代码
-
第一步:生成一个绝对随机的种子create_seed(),该种子用于生成私钥。定义create_seed函数,生成种子。
import ecdsa import random import hashlib import base58 from hashlib import sha256 def create_seed(): """ 创建绝对随机的种子 :return: string绝对随机数 :return:随机数 """ return ''.join(random.sample ('abcdefghijklmnopqrstuvwxyz!@#$%^&*()',32)). encode()
-
第二步:通过特定的非对称加密算法对种子加密生成公钥,定义create_private-key函数,生成私钥,输入参数为生成的种子
def create_private_key(seed): """ 使用种子创建密钥 :param seed:创建私钥需要的随机数 :return :以pem形式保存的私钥 """ return ecdsa.SigningKey.from_string(seed,curve=ecdsa.SECP256k1).to_pem()
-
第三步:基于已生成的私钥借助非对称加密算法生成公钥,定义create_public_key函数,生成公钥,输入的参数为生成的私钥.
def create_public_key(private_key): """ 使用私钥生成公钥 :param private_key:生成公钥需要的私钥 :return:以pem形式保存的公钥 """ return ecdsa.SigningKey.from_pem(private_key) .verifying_key.to_pem()
-
第四步:通过公钥生成地址
-
创建create_account函数,利用SHA-256将公钥进行哈希处理后得到第一个哈希值作为中间哈希值
def create_account(): """ 创建区块链用户地址 1.如果代码存在飘红内容,将鼠标移动到飘红点,查看问题,一般性为变量命名错误,或者是包未引入,修改 2.如果存在base58这个包无法解析,解决方法为:pip install base58 """ seed = create_seed() private_key_pem =create_private_key(seed) public_key_pem = create_public_key(private_key_pem) in_public_key = ecdsa.VerifyingKey.from_pem(public_key_pem).to_string() intermediate =hashlib.sha256(in_public_key).digest() #中间哈希值
-
将中间哈希值(intermediate)通过RIPMD-160及连续两次SHA-256哈希处理得到第二个哈希值(double_hash)
ripemd160=hashlib.new('ripemd160') ripemd160.update(intermediate) hash160 = ripemd160.digest() double_hash = hashlib.sha256(hashlib.sha256(hash160).digest()).digest()
-
取double_hash的前4位以及intermediate哈希值组合后进行base58编码得到地址。
#取前四位,作为校验值 checksum = double_hash[:4] #将中间哈希与校验值拼接 pre_address = hash160 +checksum #通过base58编码生成地址 address = base58.b58encode(pre_address) print(f"生成地址是:{address.decode()}")
-
-
将区块链账户三要素返回adress,private_key,public_key
#将区块链账户三要素返回:adress、private_key、public_key return{ "address":address.decode(), 'private_key':private_key_pem.decode(), 'public_key':public_key_pem.decode() }
-
验证
print(create_account())
-
以上就生成了一个完整的地址。
-
5.区块中交易产生的过程
1).区块打包交易过程
创建交易----签名解锁----广播交易-----验证交易----构建区块-----争夺记账权----广播新区块
2).交易在区块中的数据的存储方式
区块结构体中“默克根”(Merkle Root)的生成方式。如下代码为基于Python代码的默克根生成方式。构建calc_ merkle _root 函数,将需要打包的交易数组作为输入参数,通过循环的方式将交易的哈希值两两组合,通过哈希处理生成新的哈希值并更新数组,以此方式周而复始当最终生成的数组长度为1时将第一个元素作为默克根输出,如下代码为Python 的实现方式:
#这个是交易在区块中的数据存储方式
import hashlib
class MerkleTree(object):
def __init__(self, tx_list):
self.tx_list = tx_list
def calc_merkle_root(self):
"""计算Merkle根
"""
calc_txs = self.tx_list
if len(calc_txs) == 1:
# 如果交易长度为1,直接返回交易值
return calc_txs[0]
# 如果交易长度大于1
while len(calc_txs) > 1:
# 若列表中的交易个数为奇数个,则在列表最后复制最后一个交易信息进行补全
if len(calc_txs) % 2 == 1:
calc_txs.append(calc_txs[-1])
sub_hash_roots = []
# 每两个交易进行组合,生成新的哈希值
for i in range(0, len(calc_txs), 2):
# 生成的哈希值进行累加
sub_hash_roots.append(hashlib.sha256("".join(calc_txs[i:i+2]).encode()).hexdigest())
# 将累加为新的哈希列表赋值为下一次循环的内容
calc_txs = sub_hash_roots
return calc_txs[0]
# 验证
tx_list = ['3','6','7','1',"5"]
m_t = MerkleTree(tx_list)
merckle_root = m_t.calc_merkle_root();
print(merckle_root)
3).基于calc_merkle_root实现默克尔树运行情况监控
-
监控内容为calc_txs的值变化
-
定义输出的内容:
-
在进入循环处理前的值为:
-
在进入循环后处理结果(第一次)
因为当前交易的数量为5,为奇数,由于当前交易数量为5,为奇数,所以需要在列表最后复制最后一个交易信息进行补全,将每两个交易进行组合,生成新的哈希值。将新的哈希值列表赋值给calc_txs,作为下一次循环的交易列表
-
第二次循环处理记录
当前交易的值为2,不是1,需要进行下一次循环
-
第三次循环处理记录
在第三次循环中,由于calc_txs 的长度为 2,不为 1,因此需要进行下一次的循环.
-
默克尔根的取值
创建一个交易列表(tx_list),创建一个MerkleTree类的实例m_t,并将交易列表传递给它进行初始化。调用calc_merkle_root方法计算Merkle根,并将结果存储在merckle_root变量中,将Merkle根打印输出。
6.交易的验证方法
完整代码:
# -*- coding: utf-8 -*-
# File : block_verify.py
# Author: taoyahui
# Date : 2022/3/22
import models
import crypto_util
from datetime import datetime
import utils
# 1. 创建区块链
blockchain = models.Blockchain()
# 2.创建两个账户,一个模拟发送账户一个模拟接收账户
print(f"{'*' * 10}生成第一账户{'*' * 10}")
send_account = crypto_util.create_account()
print(f"{'*' * 10}生成第二账户{'*' * 10}")
recv_account = crypto_util.create_account()
# 3.加入10条交易记录
tx_list = []
for index in range(0, 10):
tx_list.append(models.Transaction(send_account['address'],
recv_account['address'],
f"第{index}条测试交易",
datetime.now(),
send_account['private_key']))
# 4. 将交易打包进入区块链
index = blockchain.add_new_block(data=tx_list)
block1 = blockchain.query_block_info(1)
block_1_merkle_root = block1['merkle_root']
print(f"第一个区块的Merkle Root为: {block_1_merkle_root}")
# 5. 验证
verify_merkle_root = utils.calc_merkle_root(block1['data'])
print(f"验证的Merkle Root为: {verify_merkle_root}")
if verify_merkle_root == block_1_merkle_root:
print("验证成功")
else:
print("验证失败")
第一步:
创建区块链,创建一个名为blockchain区块链对象,是一个空的区块链。
blockchain = models.Blockchain()
第二步:创建两个账户,一个模拟发送账户一个模拟接受账户。
-
调用crypto_utils模块的create_account方法生成第一个账户,并将结果存储在send_account变量中,用于模拟发送账户。
print(f"{'*' * 10}生成第一账户{'*' * 10}") send_account = crypto_utils.create_account()
-
调用crypto_utils模块的create_account方法生成第二个账户,并将结果存储在rec_account变量中,用于模拟接收账户.
print(f"{'*' * 10}生成第二账户{'*' * 10}") rec_account = crypto_utils.create_account()
第三步
使用for循环语句,加入10条交易记录,并添加到tx_list中
-
使用models模块的Transaction函数创建一个交易实例tx,进行交易初始化,该交易包含发送账户地址、接收账户地址、交易的内容、交易的时间戳、发送者的私钥
第四步:
将交易打包进入区块链
第五步:
定义一个名为Blockchain的类,作为区块链的实现,初始化了一个空的链chain,并调用create_genesis_block方法创建创世区块,将其添加到链中。Add_block用于添加新的区块Query_block_info用于通过索引值查询区块链chain中的区块信息Create_genesis_block创建创世区块
# 区块链对象,包括一个以chain为对象的数组
class Blockchain(object):
def __init__(self):
"""
初始化区块链对象,操作包括:
1、定义一个以chain命名的区块链数组
2、在链中加入创世区块(genesis block)
"""
self.chain = []
self.create_genesis_block()
def add_block(self, block):
self.chain.append(block)
def query_block_info(self, index=0):
"""
通过索引值查询区块链chain中的区块信息
"""
block_json = self.chain[index].to_json()
return block_json
def create_genesis_block(self):
"""
创建创世区块
"""
tx = Transaction("0" * 32, "0" * 32, "第一笔交易", datetime.now(), d_pk)
genesis_block = Block(0,
"0" * 64,
[tx],
datetime.now(),
INITIAL_BITS)
self.add_block(genesis_block)
def add_new_block(self, data):
last_block = self.chain[-1]
block = Block(last_block.index + 1,
last_block.block_hash,
data,
datetime.now(),
last_block.bits)
self.chain.append(block)
return last_block.index + 1
第六步:
验证
这里使用utils.calc_merkle_root方法计算根据交易数据计算得到的 Merkle Root,并与之前的区块中的 Merkle Root 进行比较。如果相等,则输出“验证成功”,否则输出 “验证失败”。