搭建实现账户和交易操作的区块链示例系统

搭建实现账户和交易操作的区块链示例系统
第一步:

在项目中创建crypto_utils.py文件,并在其中定义相应的函数及功能。

#账户公钥和私钥的生成方式
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()
    
def create_private_key(seed):
    """
    使用种子创建密钥
    :param seed:创建私钥需要的随机数
    :return :以pem形式保存的私钥
    """
    return ecdsa.SigningKey.from_string(seed,curve=ecdsa.SECP256k1).to_pem()

def create_public_key(private_key):
    """
    使用私钥生成公钥
    :param private_key:生成公钥需要的私钥
    :return:以pem形式保存的公钥
    """
    return ecdsa.SigningKey.from_pem(private_key).verifying_key.to_pem()

def sha256d(string):
    """
    双重 SHA-256 哈希计算
    将字符串转为哈希值
    :param string: 需要计算的字符串(bytes 或 str 类型)
    :return: 计算结果的 16 进制字符串形式

    """
    if not isinstance(string, bytes):
        string = string.encode()

    return sha256 (sha256(string).digest()).hexdigest()
#地址生成的具体过程如下:
# 1.利用SHA-256将公开的密钥进行哈希处理生成哈希值
# 2.将第一步中的哈希值通过RT\IPEMD-160进行哈希处理后生成哈希值
# 3.将第二步中的哈希值记性Base58编码
def create_account():
    """
    创建区块链用户地址
    1.如果代码存在飘红内容,将鼠标移动到飘红点,查看问题,一般性为变量命名错误,或者是包未引入,修改
    2.如果存在base58这个包无法解析,解决方法为:pip install base58
    """ 
    new_seed = create_seed()
    private_key_pem =create_private_key(new_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()  #中间哈希值
    #作二次hash生成double_hash
    ripemd160=hashlib.new('ripemd160')
    ripemd160.update(intermediate)
    hash160 = ripemd160.digest()
    double_hash = hashlib.sha256(hashlib.sha256(hash160).digest()).digest()
    #取前四位,作为校验值
    checksum = double_hash[:4]
    #将中间哈希与校验值拼接
    pre_address = hash160 +checksum
    #通过base58编码生成地址
    address = base58.b58encode(pre_address)
    print(f"生成地址是:{address.decode()}")
    #将区块链账户三要素返回:adress、private_key、public_key
    return{
        "address":address.decode(),
        'private_key':private_key_pem.decode(),
        'public_key':public_key_pem.decode()
    }
def data_sign(data, private_key):
    """
    签名
    :param data: 签名时使用的数据
    :param private_key: 签名时使用的私钥
    :return:
    """
    if not isinstance(data,bytes):
        data = data.encode()
    sk = ecdsa.SigningKey.from_pem(private_key)
    sig = sk.sign(data)
    return sig
    

def data_verify(data, sig, public_key):
    """
    验证签名
    :param data: 验证时使用的数据
    :param sig: 要验证的签名
    :param public_key: 验证时使用的公钥
    :return: 验证结果,返回0表示成功,返回1表示失败,返回2表示出现问题
    """
    if not isinstance(data,bytes):
        data = data.encode()

    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

第二步:

创建区块链的相关对象

在项目中创建models.py文件,并在其中定义区块链对象。

import hashlib
from datetime import datetime
from crypto_utils import data_sign,sha256d
import binascii
INITIAL_BITS=0x1e777777  #此配置为模块真实区块的数据设置(预留)
#预设一个私钥,用于创建创世区块使用
d_pk="""-----BEGIN EC PRIVATE KEY-----\nMHQCAQEEIClncSpsc2Fua3ljeiR2aCNydSFkbXQoQHAleGZl
YiZqoAcGBSuBBAAK\noUQDQgAEyTx/sAlhdNUOwcfnCjOVp9fxMF6DUwSLKFqj2E6sDFuPVrKF9wVWH8J3\nntxWh+kR3GFKcB48v3eTfElUs5L7Zw==\n
-----END EC PRTVATE KEY -----\n
"""
class Transaction (object):
    def __init__(self,sender,recipient,data,timestamp,private_key,):
     """
     交易初始化
     :param sender:发送者的地址
     :param recipient:接收者的地址
     :param data:交易的内容
     :param timestamp:交易的时间戳
     :param private_key:发送者的私钥
     """
     self.sender = sender
     self.recipient=recipient
     self.data = data
     self.timestamp = timestamp
     #生成交易的哈希值
     self.id=sha256d(self.to_string())
     #生成签名
     self.sig = data_sign(self.id,private_key)

    def to_string(self):
       """
       将交易元素拼接为一个字符串
       :return:
       """
       return f"{self.sender}{self.recipient}{self.data}"  f"{self.timestamp.strftime('%Y/%m/%d %H:%M:%S')}"

    def to_json(self):
       """
       将交易转换变为一个DICT对象
       :return:
       """
       return {
          "id":self.id,
          "sender":self.sender,
          "recipient":self.recipient,
          "data":self.data,
          "timestamp":self.timestamp.strftime('%Y/%m/%d %H:%M:%S'),
          "sig":binascii.hexlify(self.sig).decode(),         
       }

#区块对象
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.merkle_root=self.calc_merkle_root()
        #计算区块哈希值
        self.block_hash=self.calc_block_hash()
    def to_json(self):
        """
        将区块内容以JSON的形式输出
        :return:
        """
        tx_list = [tx.to_json() for tx in self.data]
        # tx_json = json.dumps(tx_list, indent=3)
        return {
            "index": self.index,
            "prev_hash": self.prev_hash,
            "merkle_root": self.merkle_root,
            "data": tx_list,
            "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_merkle_root(self):
        """
        计算默克树的根(Merkle Root)
        :return:
        """
        calc_txs = [tx.id for tx in self.data]
        if len(calc_txs) == 1:
            return calc_txs[0]
        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):
                join_str = "".join(calc_txs[i:i+2])
                sub_hash_roots.append(hashlib.sha256(join_str.encode()).hexdigest())
            calc_txs = sub_hash_roots
        return calc_txs[0]
    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
# 区块链对象,包括一个以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


# 模拟的节点
class Peer:
    def __init__(self):
        self.tx_pool = []

    def add_tx(self, tx):
        self.tx_pool.append(tx)

    def clear_pool(self):
        self.tx_pool = []
   

在代码中定义了d_pk变量,其作用为项目启动时内置的私钥,用于创建创世区块时创建第一笔交易时使用。

第三步:

添加项目的其他功能

在项目中创建会创建utils.py文件,用于添加一些通用的方法,本项目中calc_merkle_root用于验证区块数据的正确性。

from hashlib import sha256

def calc_merkle_root(data):
    """
    计算默克树的根(Merkle Root)
    :return:
    """
    calc_txs = [tx.id for tx in data]
    if len(calc_txs) == 1:
        return calc_txs[0]
    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):
            join_str = "".join(calc_txs[i:i + 2])
            sub_hash_roots.append(sha256(join_str.encode()).hexdigest())
        calc_txs = sub_hash_roots
    return calc_txs[0]

第四步:

创建项目接口,使用Postman进行验证接口正确性

from models import Transaction, Blockchain,Peer
from datetime import datetime
from flask import Flask,request,jsonify
import crypto_utils
import utils

app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False

#空数据返回内容
empty_res={
    'code':404,
    'data':'empty'
}

#执行成功返回内容
success_res={
    'code':200,
    'data':'ok'
}

#创建区块链
blockchain = Blockchain()

#创建一个模拟节点
peer = Peer()

@app.route("/account_create",methods=['GET'])
def account_create():
    """
    创建账户接口
    :return:
    """
    return jsonify({
        'code':200,
        'data':crypto_utils.create_account()
    })

@app.route('/add_transaction',methods=['POST'])
def add_transaction():
    """
    新增一笔交易
    :return:
    """
    body = request.json
    if 'sender'not in body or 'recipient' not in body \
          or 'data' not in body or 'private_key' not in body:
        return jsonify(empty_res)
    new_transaction = Transaction(body['sender'],
                                  body['recipient'],
                                  body['data'],
                                  datetime.now(),
                                  body['private_key'])
    peer.tx_pool.append(new_transaction)
    return jsonify(success_res)

@app.route('/add_block',methods=['GET'])
def add():
    """
    添加新区快接口,将peer节点的交易缓存数据加入新区块中
    """
    tx_list=peer.tx_pool
    index = blockchain.add_new_block(tx_list)
    peer.tx_pool=[]
    #数据返回内容,在data中加入新加入的区块的索引
    return jsonify({
        'code':200,
        "data":index
    })
@app.route('/query_block',methods=['GET'])
def query_block():
    """
    区块查询接口,通过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)
    })
@app.route('/query_tx',methods=['GET'])
def query_tx():
    """
    查询交易接口
    :return:
    """
    id = request.args['id']
    for block in blockchain.chain:
        for tx in block.data:
            if tx.id == id:
                return jsonify({
                    'code':200,
                    "data":tx.to_json()
                })
    return jsonify({
        'code':404,
        'data':'未查询到相关信息'
    })

@app.route('/validate_tx',methods=['POST'])
def validate_tx():
    """
    验证交易接口,需要通过post传递交易哈希(id)和公钥(public-key)
    """
    res_json= request.json 
    if 'id' not in res_json or 'public_key' not in res_json:
        return jsonify(empty_res)
    tx_id = res_json['id']
    for block in blockchain.chain:
        for tx in block.data:
            if tx.id == tx_id:
                verification_res = crypto_utils.data_verify(tx.id,tx.sig,res_json['public_key'])
                if verification_res ==0:
                    return jsonify({
                        'code':200,
                        "data":'验证成功'
                    })
                else:
                    return jsonify({
                        'code':500,
                        "data":'验证失败!'
                    })

@app.route('/validate_block', methods=['GET'])
def validate_block():
    """
    验证区块接口
    :return:
    """
    index = int(request.args['index'])
    block = blockchain.chain[index]
    root = block.merkle_root
    calc_root = utils.calc_merkle_root(block.data)
    if root == calc_root:
        return jsonify({
            'code':200,
            'data':'ok'
        })
    else:
        return jsonify({
            'code':500,
            'data':'结果不符'
        })


if __name__ =='__main__':
    app.run()
(1).创建账户

接口路由:/account_create 方法:GET

响应参数:JSON形式

Code:200表示正确返回,否则为错误返回

Data:账户三要素分别为:

Address:账户地址

Private_key:账户私钥

Public_key:账户公钥

验证创建账户功能,当有如code返回200则表示接口相应正确

(2)新增交易

接口路由:/add_transaction

方法:POST

请求参数:JSON形式,sender:交易发起者地址,recipient:交易接收者地址, data:交易的实际内容,private_key:交易发起者的私钥

响应参数:code:200正确返回,否则返回错误,data:返回的具体信息

使用postman调用配置,当前有code返回200则表示接口相应正确

(3).添加新区块接口

接口路由:/add_block

方法:GET

响应参数: code:200正确返回,否则返回错误,data:新增区块索引

验证区块新增接口,使用postman调用配置,当前有code返回200则表示接口相应正确

(4)区块查询

接口路由:/query_block

方法:GET

请求参数:GET请求参数,index:区块索引值

响应参数: code:200正确返回,否则返回错误,data:查询区块数据

验证区块查询接口,使用postman调用配置,当前有code返回200则表示接口相应正确

查询区块高度为上一次新增区块索引的区块内容。

(5).交易查询

接口路由:/query_tx

方法:GET

请求参数:GET请求参数,id:查询交易哈希值

响应参数: code:200正确返回,否则返回错误,data:查询交易的数据

验证查询交易接口,以GET请求传输交易哈希值,使用postman调用配置。

(6).验证交易

接口路由:/validate_tx

方法:POST

请求参数:id:查询交易哈希值,public_key发送账户的公钥

响应参数: code:200正确返回,否则返回错误,data:返回具体的信息

验证交易验证接口,使用postman以POST形式发送交易哈希值和账户A的公钥,当前有code返回200则表示接口相应正确

(7).验证区块

接口路由:/validate_block

方法:GET

请求参数:GET请求参数,index区块索引值

响应参数: code:200正确返回,否则返回错误,data:返回的具体信息。

验证区块查询接口,使用postman调用配置,当前有code返回200则表示接口相应正确

验证高度为0。

通过以上构建系统的区块、交易、区块链等对象,实现创世区块的创建、默克尔根计算以及基于私钥的交易哈希生成等特殊操作,然后使用Flask等Web服务去框架运行持久化进程,实现·账户创建(地址,公钥,私钥),新增交易(体现交易缓存功能),新增区块(区块打包功能),区块查询,交易查询,交易验证,区块的验证。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值