实验目标
以Web服务的形式持久化运行区块链,并通过HTTP接口的形式实现对区块链的操作。
实验内容
- 构建区块链的区块对象和区块链对象。
- 使用Flask等Web服务框架运行持久化的进程,实现基于HTTP接口实现新区块的添加功能以及传递区块索引查询区块链中的区块功能。
实验步骤
1、创建项目以及依赖
1)打开PyCharm创建一个项目simple_blockchain,存储在桌面上,采用IDE的虚拟环境。
2)安装相关依赖包,json、hashlib、datetime以及flask(执行pip install flask)
2、创建区块对象和区块链对象
1) 区块由区块头和区块体两个部分构成,且通过哈希链以JSON格式相互链接在一起。
于是创建区块对象的代码应包括如下三部分:
class Block(object):
def __init__(self, index, prev_hash, data, timestamp, bits):
"""
区块的初始化方法,在创建一个区块需传入包括索引号等相关信息
:param index: 区块索引号
:param prev_hash: 前一区块的哈希值
:param data: 区块中需保存的记录
:param timestamp: 区块生成的时间戳
:param bits: 区块需传入的比特值(预留)
"""
def to_json(self):
"""
将区块内容以JSON的形式输出
:return:
"""
def calc_block_hash(self):
"""
生成区块对应的哈希值
:return:
"""
2)在区块链系统中,数据的存储以账本的方式实现,账本中包含许多区块,区块间以链式的方式两两相连。
于是创建区块链对象的代码应包括如下五部分:
class Blockchain(object):
def __init__(self):
"""
初始化区块链对象,操作包括:
1、定义一个以chain命名的区块链数组
2、在链中加入创世区块(genesis block)
"""
def add_block(self, block):
"""
将新的区块加入区块链chain中,该方法将不被外界调用
:param block: 新加入的区块
:return:
"""
def query_block_info(self, index=0):
"""
通过索引值查询区块链chain中的区块信息
:param index: 查询区块的索引值
:return:
"""
def create_genesis_block(self):
"""
创建创世区块,创世区块内容如下:
index -> 设置为0,代表第一个区块
prev_hash -> 设置为64个"0"作为默认参数
data -> 存储一段字符串
:return:
"""
def add_new_block(self, data):
"""
可供调用的方法,用于添加新的区块
:param data:
:return:
"""
3、编写与实现相应HTTP接口,实现账本添加和查询功能。
1)将第2步创建好的区块对象和区块链对象作为区块链账本系统模型models,并测试该模型是否构建成功,测试代码test.py如下:
import models
blockchain = models.Blockchain()
print(blockchain.query_block_info(0))
from time import sleep
sleep(1)
blockchain.add_new_block("新增的区块")
print(blockchain.query_block_info(1))
执行输出结果如下:
2) 在app.py文件中加入区块添加和区块查询的HTTP请求响应,一定要引用models
@app.route('/add', methods=['POST'])
def add():
"""
区块链添加功能API
:return:
"""
body = request.json
index = blockchain.add_new_block(body['data'])
return json.dumps({
'code':200,
'data':index
})
4、使用Postman进行验证接口正确性
1)区块添加功能
2)区块查询功能
实验
- 以寝室为单位,创建一个姓名区块链。(运行截图)
比如一个寝室4个人,张三——李四——王五——钱六
import hashlib
import json
import timeclass Block:def __init__(self, index, data, prev_hash):self.index = indexself.timestamp = time.strftime("%Y/%m/%d %H:%M:%S")self.data = data # 寝室成员姓名self.prev_hash = prev_hashself.nonce = "00000000"self.bits = "1e777777"self.hash = self.calculate_hash()def calculate_hash(self):# 计算区块的哈希值block_string = json.dumps({"index": self.index,"timestamp": self.timestamp,"data": self.data,"prev_hash": self.prev_hash,"nonce": self.nonce,"bits": self.bits}, sort_keys=True).encode()return hashlib.sha256(block_string).hexdigest()class Blockchain:def __init__(self):# 创建创世区块self.chain = [self.create_genesis_block()]def create_genesis_block(self):# 创建创世区块return Block(0, "这是第一个区块(创世区块)", "0" * 64)def get_latest_block(self):# 获取最新的区块return self.chain[-1]def add_block(self, data):# 添加新区块prev_block = self.get_latest_block()new_block = Block(prev_block.index + 1, data, prev_block.hash)self.chain.append(new_block)def is_chain_valid(self):# 验证区块链的完整性for i in range(1, len(self.chain)):current_block = self.chain[i]prev_block = self.chain[i-1]# 验证当前区块的哈希值if current_block.hash != current_block.calculate_hash():return False# 验证区块链的连续性if current_block.prev_hash != prev_block.hash:return Falsereturn Truedef display_chain(self):# 显示区块链的所有区块for block in self.chain:print(json.dumps({"index": block.index,"timestamp": block.timestamp,"data": block.data,"hash": block.hash,"prev_hash": block.prev_hash,"nonce": block.nonce,"bits": block.bits}, indent=2, ensure_ascii=False))print()# 创建区块链并添加寝室成员
def main():# 创建区块链实例dorm_chain = Blockchain()# 添加寝室成员members = [..."lvy"]# 将每个成员添加到区块链中for member in members:dorm_chain.add_block(member)# 显示整个区块链print("寝室成员区块链:")print("=" * 50)dorm_chain.display_chain()# 验证区块链的完整性print(f"区块链是否有效: {dorm_chain.is_chain_valid()}")if __name__ == "__main__":main()
- 利用HTTP接口实现GET查询“学号+姓名”的信息。(运行截图)
from flask import Flask, jsonify
from blockchain import Blockchainapp = Flask(__name__)# 创建区块链实例
blockchain = Blockchain()# 初始化区块链数据
def init_blockchain():# 添加寝室成员(学号+姓名)members = ["000000000000+lvy","111111111111+张三","222222222222+李四","333333333333+王五"]# 将每个成员添加到区块链中for member in members:blockchain.add_block(member)# 初始化区块链
init_blockchain()@app.route('/get_chain', methods=['GET'])
def get_chain():"""获取完整的区块链"""response = {'chain': [],'length': len(blockchain.chain)}for block in blockchain.chain:response['chain'].append({'index': block.index,'timestamp': block.timestamp,'data': block.data,'hash': block.hash,'prev_hash': block.prev_hash,'nonce': block.nonce,'bits': block.bits})return jsonify(response), 200@app.route('/get_member/<student_id>', methods=['GET'])
def get_member(student_id):"""根据学号查询成员信息"""for block in blockchain.chain:if block.data.startswith(student_id):return jsonify({'status': 'success','data': block.data,'block_info': {'index': block.index,'timestamp': block.timestamp,'hash': block.hash}}), 200return jsonify({'status': 'error','message': '未找到该学号对应的信息'}), 404if __name__ == '__main__':app.run(debug=True, port=5000)