区块链开发

一.开发环境准备

  • centos7及以上/Ubuntu18.04及以上
  • python3.6及以上
  •  pip20.0.4及以上
  • pyCharm 或vscode代码编辑器
  • Postman接口测试工具

1.curl命令 

curl能访问远程服务器并从远程服务器下载数据,也可发送数据

curl工具在Ubuntu安装命令

apt install curl
#或者可以使用
sudo snap install curl

centos安装命令

yum install curl 

2.tree命令 

在Ubuntu安装命令

sudo apt install tree

在centos安装命令

yum install tree
  •  -a :显示所有文件和模式
  • -L :显示层级数量
  • -d:显示目录名称
  • -D:列出文件或目录的更改时间
  • -p:列出权限
  • -s:列出文件或目录的大小
  • -t:按文件更改时间排序

3.jq命令

#Ubuntu
sudo apt-get install jq
#centos
yum install -y epel-release
yum install -y jq

 4.在python web服务端

安装flask

pip install Flask

创建一个post/get接口,具体代码可以看Flask框架_南栀北夏ii的博客-CSDN博客

二.密码学基础

1.哈希算法

概念:任意长度的输入转换为固定长度输出的一种方法

特性:抗碰撞型,不可逆性

种类:MD5,SHA-256,SM3

以下代码为使用hashlib的python实现:

#1 引用hanshlib依赖包
import hashlib
# 2。定义需要加密的变量
words = 'Hello World!'
# 3,使用sha256算法加密
hash_code = hashlib.sha256(words.encode()).hexdigest()
# 4.打印加密后的结果
print(hash_code)  #7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069

2.非对称加密 算法

公钥加密,私钥解密

私钥验签,公钥解密(私钥签名,公钥验签)

椭圆曲线加密算法(ECC)签名与验签用python实现:

安装ecdsa依赖

pip install ecdsa

import ecdsa
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
def verify(data,sig,public_key):
    """
    验签
    :param data:验签时使用的数据
    :param sig:验签时使用的签名
    :param public_key:验签时使用的公钥
    :return:
    """
    vk = ecdsa.VerifyingKey.from_pem(public_key)
    try:
        if vk.verify(sig,data):
            return "验签成功"
        else:
            return "验签失败"
    except Exception as e:
        return "验签过程出现问题" 
    
import random
string = "hello world"
private_key = ''.join(random.sample('abcdefghijklmnopqrstuvwxyz!@#$%^&*()',32)).encode()
print(f'私钥是:{private_key}')
public_key = ecdsa.SigningKey.from_string(private_key,curve=ecdsa.SECP256k1).verifying_key.to_pem()
print(f'公钥是:{public_key}')
sig = sign(string.encode(),private_key)
print(f'签名是:{sig}')
print(verify(string.encode(),sig,public_key))

 三.实战一:构建具备加密功能的Flask服务端

1.构建哈希算法的处理函数

import hashlib
def hash_encrypt(input):
    """
    使用哈希算法加密
    :param input:客户端发送的数据
    :return:返回给客户端的加密数据
    """
    hash_code = hashlib.sha256(input.encode()).hexdigest()
    return hash_code

2.在程序运行入口app.py中加入哈希算法处理的HTTP请求响应

from flask import Flask,request
# 导入你加密的哈希算法函数文件
import shixun_1


app = Flask(__name__)

@app.route("/")
def index():
    return "hello world"

@app.route("/encrypt",methods=['GET'])
def encrypt():
    """
    哈希函数加密
    :return:加密的字典对象
    """
    #请求获取名为data的数据
    data = request.args.get('data')
    #运用shixun_1的hash_encrypt函数对data数据经行哈希加密
    res = shixun_1.hash_encrypt(data)
    #返回加密的数据
    return {
        'res':res
    }

if __name__ == "__main__":
    app.run()

3,运行python文件后,打开Postman验证开发接口

四.区块链的区块与账本

区块:用于存储电子信息的载体

区块:区块头和区块体

区块头:父块哈希,默克尔跟,挖矿难度,随机数,时间戳

区块体:交易记录,交易数量

区块链账本:区块(账页),区块体(账目),区块头(账目相关的关键信息)

1.账本的具体实现(创世区块)

  • Block对象:代表区块的概念
  • Blockchain对象:代表一个简易版的区块链

注意下面的代码文件名为qulian.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 qulian
# 创建区块链
blockchain = qulian.Blockchain()
# 查询区块高度为0,创世区块信息
print(blockchain.query_block_info(0))
from time import sleep
sleep(1)
# 添加新区快,高度为1
blockchain.add_new_block("新增区块")
print(blockchain.query_block_info(1))

2.实战二:构建简单的区块链账本系统

  • 创建shixun_2.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
  •  编写Flask项目的HTTP接口,创建app_1.py文件
# 导入你的区块链的代码
import shixun_2
from flask import Flask,request,jsonify
app_1 = Flask(__name__)
app_1.config['JSON_AS_ASCII'] = False
blockchain = shixun_2.Blockchain()

@app_1.route('/add',methods=['POST'])
def add():
    """
    添加新区快接口,接口形式为POST
    请求时通过Body中加入类似{"data":"..."}的形式作为新区快的内容
    :return:
    """
    body = request.json
    index = blockchain.add_new_block(body['data'])
    # 数据返回内容,在data中加入新区快的索引
    return jsonify({
        'code':200,
        'data':index
    })
@app_1.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_1.run()

  • 用postman验证功能正确性

五.区块链的账户和交易

 1.区块链中的账户

账户包含三要素:地址,公钥,私钥

账户公钥私钥的生成方式:


import ecdsa
import random
# 1.生成种子(随机数)
seed = ''.join(random.sample('abcdefghijklmnopqrstuvwxyz!@#$%^&*()',32)).encode()
# 2.生成私钥
private_key = ecdsa.SigningKey.from_string(seed,curve=ecdsa.SECP256k1).to_pem()
# 3.生成公钥
publish_key = ecdsa.SigningKey.from_pem(private_key).verifying_key.to_pem()
# 4.输出打印
print(f'{private_key}')
print(f'{publish_key}')

账户地址的生成方式:


import ecdsa 
import random
import hashlib
import base58
from hashlib import sha256
def create_seed():
    """
    创建密钥对应的种子
    :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_publish_key(private_key):
    """
    使用私钥生成公钥
    :param private_key:生成公钥需要的私钥
    :return:以pem的形式保存公钥
    """
    return ecdsa.SigningKey.from_pem(private_key).verifying_key.to_pem()

# 地址生成步骤如下:
# 1.利用SCA-256将公开密钥进行哈希处理生成哈希值
# 2.将第一步中的哈希值通过RIPEMD-160进行哈希处理后生成新的哈希值
# 3.将第二步中的哈希值记作Base58编码

def create_account():
    """
    生成账户
    :return:账户三要素:地址、私钥、公钥
    """
    new_seed = create_seed()
    private_key_pem = create_private_key(new_seed)
    public_key_pem = create_publish_key(private_key_pem)
    # 中间哈希值
    in_public_key = ecdsa.VerifyingKey.from_pem(public_key_pem).to_string()
    intermediate = hashlib.sha256(in_public_key).digest()
    # 作二次hash生成doubl_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()}')
    return{
        'address':address.decode(),
        'private_key':private_key_pem.decode(),
        'public_key':public_key_pem.decode()
    }

# 验证
print(create_account())

 

2.区块链中的交易

交易:一种特殊的数据存贮结构

交易在区块中的数据存贮方式

交易数据以默克尔根的形式进行存贮:


import hashlib
class MerkleTree(object):
    def __init__(self,tx_list):
        self.tx_list = tx_list

    def calc_markle_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 = ['1','2','3','4','5']
m_t = MerkleTree(tx_list)
merckle_root = m_t.calc_markle_root();
print(merckle_root)

3.实战三:交易对象的创建

注意:交易的验证方法:私钥签名,公钥验签


import ecdsa 
import random
import hashlib
import base58
from hashlib import sha256
def create_seed():
    """
    创建密钥对应的种子
    :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_publish_key(private_key):
    """
    使用私钥生成公钥
    :param private_key:生成公钥需要的私钥
    :return:以pem的形式保存公钥
    """
    return ecdsa.SigningKey.from_pem(private_key).verifying_key.to_pem()

def sha256d(string):
    """
    将字符串转为哈希值
    :param string:
    :return:
    """
    if not isinstance(string,bytes):
        string = string.encode()
    return sha256(sha256(string).digest()).hexdigest()

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:
    """
    if not isinstance(data, bytes):
        data = data.encode()
    vk = ecdsa.VerifyingKey.from_pem(public_key)
    try:
        if vk.verify(sig,data):
            return "验签成功"
        else:
            return "验签失败"
    except Exception as e:
        print(e)
        return "验签过程出现问题" 
# 地址生成步骤如下:
# 1.利用SCA-256将公开密钥进行哈希处理生成哈希值
# 2.将第一步中的哈希值通过RIPEMD-160进行哈希处理后生成新的哈希值
# 3.将第二步中的哈希值记作Base58编码

def create_account():
    """
    生成账户
    :return:账户三要素:地址、私钥、公钥
    """
    new_seed = create_seed()
    private_key_pem = create_private_key(new_seed)
    public_key_pem = create_publish_key(private_key_pem)
    # 中间哈希值
    in_public_key = ecdsa.VerifyingKey.from_pem(public_key_pem).to_string()
    intermediate = hashlib.sha256(in_public_key).digest()
    # 作二次hash生成doubl_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()}')
    return{
        'address':address.decode(),
        'private_key':private_key_pem.decode(),
        'public_key':public_key_pem.decode()
    }

# 验证
print(create_account())


import hashlib
from datetime import datetime
from crypto_utils import data_sign,sha256d
import binascii

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(),

        }


import crypto_utils 
import model
from datetime import datetime

# 1.创建两个账户,一个模拟发送账户,一个模拟接受账户
print(f"{'*' * 10}生成第一个账户{'*' * 10}")
send_account = crypto_utils.create_account()
print(f"{'*' * 10}生成第二个账户{'*' * 10}")
rec_account = crypto_utils.create_account()
# 2.生成一个测试交易内容
tx = model.Transaction(send_account['address'],
                       rec_account['address'],
                       "测试交易",datetime.now(),send_account['private_key']
                       )

# 3.验证交易正确性
verification_res = crypto_utils.data_verify(tx.id,tx.sig,send_account['public_key'])
print(f'验证交易,返回结果:{verification_res}')

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南栀北夏ii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值