21026 large

007 孤荷凌寒从零开始学区块链第七天信息的加密与签名及验签

941xue · 于 发布 · 99 次阅读

孤荷凌寒自学python第九十三天认识区块链007
【主要内容】
今天开始初步分析从github上获取的开源代码怎么实现简单区块链的入门知识,共用时间30分钟。
(此外整理作笔记花费了约70分钟)
详细学习过程见文末学习过程屏幕录像。
完全使用别人的代码完整的实践了一次信息在区块链上的发送接收,信息的加密与签名及验签,同时实践了一下生成区块与将交易信息写入区块(原理还不懂)。

【学习笔记】
一、Flask跨域申请模块的安装
跨域信息的获取,在flask中有相应的补充模块来实现,它就是:
flask-cors
安装方法非常简单:
pip install flask-cors
我的安装过程cmd窗口的显示:
C:\WINDOWS\system32>pip install flask-cors
Collecting flask-cors
Downloading https://files.pythonhosted.org/packages/65/cb/683f71ff8daa3aea0a5cbb276074de39f9ab66d3fbb8ad5efb5bb83e90d2/Flask_Cors-3.0.7-py2.py3-none-any.whl
Requirement already satisfied: Six in g:\w10_1\python\python365\lib\site-packages\six-1.11.0-py3.6.egg (from flask-cors) (1.11.0)
Requirement already satisfied: Flask>=0.9 in g:\w10_1\python\python365\lib\site-packages (from flask-cors) (1.0.2)
Requirement already satisfied: Jinja2>=2.10 in g:\w10_1\python\python365\lib\site-packages (from Flask>=0.9->flask-cors) (2.10)
Requirement already satisfied: Werkzeug>=0.14 in g:\w10_1\python\python365\lib\site-packages (from Flask>=0.9->flask-cors) (0.15.2)
Requirement already satisfied: itsdangerous>=0.24 in g:\w10_1\python\python365\lib\site-packages (from Flask>=0.9->flask-cors) (1.1.0)
Requirement already satisfied: click>=5.1 in g:\w10_1\python\python365\lib\site-packages (from Flask>=0.9->flask-cors) (7.0)
Requirement already satisfied: MarkupSafe>=0.23 in g:\w10_1\python\python365\lib\site-packages (from Jinja2>=2.10->Flask>=0.9->flask-cors) (1.0)
Installing collected packages: flask-cors
Successfully installed flask-cors-3.0.7

二、生成相对应的同一个钱包的公钥和私钥
(一)实现思路
1.得到随机字符串
2.生成私钥
3.根据私钥计算出公钥
(二)代码实现


    random_gen = Crypto.Random.new().read #得到随机字符串
    private_key = RSA.generate(1024, random_gen) #从随机字符串生成私钥
    public_key = private_key.publickey() #从私钥生成对应成对的公钥(即公钥和私钥之间是有对应关系的)
    response = {
        'private_key': binascii.hexlify(private_key.exportKey(format='DER')).decode('ascii'),
        'public_key': binascii.hexlify(public_key.exportKey(format='DER')).decode('ascii')
    }

三、在区块链网络中从一个节点向另一个节点发送信息(交易广播)时的加密与签名
(一)想发送信息(或想广播一笔交易的一方简称为发送者)需要在发送的消息中包含:
发送者的地址(在这儿直接用发送者的公钥来表示)
发送者的私钥签名(就是将发送者的私钥通过一定运算变成一个不可更改但又可以通过改善者公开的公钥来进行验证真伪的字符串)
发送者要发送的信息(如果是广播的交易,则就是要发送给接收者的数字代币金额)
接收者的地址(在这儿直接用接收者的公钥来表示)
(二)代码实现:


    def sign_transaction(self):
        """
        Sign transaction with private key
        """
        private_key = RSA.importKey(binascii.unhexlify(self.sender_private_key)) #对发送者的私钥进行处理
        signer = PKCS1_v1_5.new(private_key) #通过发送者的私钥来计算出发送者要添加到改善信息中的私钥的数字签名信息
        h = SHA.new(str(self.to_dict()).encode('utf8'))
        return binascii.hexlify(signer.sign(h)).decode('ascii') #这是添加有签名信息的要广播给区块链网络的信息

四、矿工接收到发送者的一次交易广播后,进行交易合法性验证(这儿只进行验签)
(一)实现思路
1.得到发送者的公钥
2.将发送者的公钥进行一定算法的计算
3.将发送者所广播信息中的签名部分与公钥进行验证,以证明数字签名的真实性
(二)代码


    def verify_transaction_signature(self, sender_address, signature, transaction):
        """
        Check that the provided signature corresponds to transaction
        signed by the public key (sender_address)
        """
        public_key = RSA.importKey(binascii.unhexlify(sender_address)) #获取发送方的公钥
        verifier = PKCS1_v1_5.new(public_key)  #使用发送方的公钥来验证发送方的签名的开始
        h = SHA.new(str(transaction).encode('utf8'))
        return verifier.verify(h, binascii.unhexlify(signature)) #验证发送方的签名 #误报错误,可以直接运行

四、对别人开源的代码进行的研究,今天作了一定的批注
(一)普通用户(只进行交易的节点)的客户端的Py实现的服务器端的代码。
代码(含批注)如下:

'''
title           : blockchain_client.py
description     : A blockchain client implemenation, with the following features
                  - Wallets generation using Public/Private key encryption (based on RSA algorithm)
                  - Generation of transactions with RSA encryption      
author          : Adil Moujahid
date_created    : 20180212
date_modified   : 20180309
version         : 0.3
usage           : python blockchain_client.py
                  python blockchain_client.py -p 8080
                  python blockchain_client.py --port 8080
python_version  : 3.6.1
Comments        : Wallet generation and transaction signature is based on [1]
References      : [1] https://github.com/julienr/ipynb_playground/blob/master/bitcoin/dumbcoin/dumbcoin.ipynb
'''


from collections import OrderedDict

import binascii

import Crypto
import Crypto.Random
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5

import requests
from flask import Flask, jsonify, request, render_template

class Transaction:

    def __init__(self, sender_address, sender_private_key, recipient_address, value):
        self.sender_address = sender_address
        self.sender_private_key = sender_private_key
        self.recipient_address = recipient_address
        self.value = value

    def __getattr__(self, attr):
        return self.data[attr]

    def to_dict(self):
        return OrderedDict({'sender_address': self.sender_address,
                            'recipient_address': self.recipient_address,
                            'value': self.value})

    def sign_transaction(self):
        """
        Sign transaction with private key
        """
        private_key = RSA.importKey(binascii.unhexlify(self.sender_private_key)) #对发送者的私钥进行处理
        signer = PKCS1_v1_5.new(private_key) #通过发送者的私钥来计算出发送者要添加到改善信息中的私钥的数字签名信息
        h = SHA.new(str(self.to_dict()).encode('utf8'))
        return binascii.hexlify(signer.sign(h)).decode('ascii') #这是添加有签名信息的要广播给区块链网络的信息


app = Flask(__name__)

@app.route('/') #这儿将index.html文件的展示目录指定为虚拟web服务器的根目录
def index():
    return render_template('./index.html')

@app.route('/make/transaction') #这儿将make_transaction.html文件的展示目录指定为虚拟web服务器的make一级目录下的transaction子目录
def make_transaction():
    return render_template('./make_transaction.html')

@app.route('/view/transactions') #这儿将view_transactions.html文件的展示目录指定为虚拟web服务器的view一级目录下的transactions子目录
def view_transaction():
    return render_template('./view_transactions.html')

@app.route('/wallet/new', methods=['GET'])
def new_wallet():
    random_gen = Crypto.Random.new().read #得到随机字符串
    private_key = RSA.generate(1024, random_gen) #从随机字符串生成私钥
    public_key = private_key.publickey() #从私钥生成对应成对的公钥(即公钥和私钥之间是有对应关系的)
    response = {
        'private_key': binascii.hexlify(private_key.exportKey(format='DER')).decode('ascii'),
        'public_key': binascii.hexlify(public_key.exportKey(format='DER')).decode('ascii')
    }

    return jsonify(response), 200

@app.route('/generate/transaction', methods=['POST'])
def generate_transaction():

    sender_address = request.form['sender_address'] #从客户端post过来的参数中,获取发送者用户公钥
    sender_private_key = request.form['sender_private_key'] #从客户端post过来的参数中,获取发送者用户私钥
    recipient_address = request.form['recipient_address'] #从客户端post过来的参数中,获取接收者用户公钥
    value = request.form['amount']  #从客户端post过来的参数中,获取本次交易要发送的代币数量

    #下一行代码生成一个transaction类的实例 对象 
    transaction = Transaction(sender_address, sender_private_key, recipient_address, value)

    response = {'transaction': transaction.to_dict(), 'signature': transaction.sign_transaction()}
        # 将广播一次交易的所有信息(发送者私钥除外)封装为字典      #这是添加发送者的私钥的签名信息

    return jsonify(response), 200

if __name__ == '__main__':
    from argparse import ArgumentParser

    parser = ArgumentParser()
    parser.add_argument('-p', '--port', default=80, type=int, help='port to listen on')
    args = parser.parse_args()
    port = args.port

    app.run(host='127.0.0.1', port=port)

(二)矿工使用的客户端的py实现的服务器端的代码(含少量批注)

'''
title           : blockchain.py
description     : A blockchain implemenation
author          : Adil Moujahid
date_created    : 20180212
date_modified   : 20180309
version         : 0.5
usage           : python blockchain.py
                  python blockchain.py -p 5000
                  python blockchain.py --port 5000
python_version  : 3.6.1
Comments        : The blockchain implementation is mostly based on [1]. 
                  I made a few modifications to the original code in order to add RSA encryption to the transactions 
                  based on [2], changed the proof of work algorithm, and added some Flask routes to interact with the 
                  blockchain from the dashboards
References      : [1] https://github.com/dvf/blockchain/blob/master/blockchain.py
                  [2] https://github.com/julienr/ipynb_playground/blob/master/bitcoin/dumbcoin/dumbcoin.ipynb
'''

from collections import OrderedDict

import binascii

import Crypto
import Crypto.Random
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5

import hashlib
import json
from time import time
from urllib.parse import urlparse
from uuid import uuid4

import requests
from flask import Flask, jsonify, request, render_template
from flask_cors import CORS


MINING_SENDER = "THE BLOCKCHAIN"
MINING_REWARD = 1
MINING_DIFFICULTY = 2

class Blockchain:

    def __init__(self):

        self.transactions = []
        self.chain = []
        self.nodes = set()
        #Generate random number to be used as node_id
        self.node_id = str(uuid4()).replace('-', '')
        #Create genesis block
        self.create_block(0, '00')

    def register_node(self, node_url):
        """
        Add a new node to the list of nodes
        """
        #Checking node_url has valid format
        parsed_url = urlparse(node_url)
        if parsed_url.netloc:
            self.nodes.add(parsed_url.netloc)
        elif parsed_url.path:
            # Accepts an URL without scheme like '192.168.0.5:5000'.
            self.nodes.add(parsed_url.path)
        else:
            raise ValueError('Invalid URL')

    def verify_transaction_signature(self, sender_address, signature, transaction):
        """
        Check that the provided signature corresponds to transaction
        signed by the public key (sender_address)
        """
        public_key = RSA.importKey(binascii.unhexlify(sender_address)) #获取发送方的公钥
        verifier = PKCS1_v1_5.new(public_key)  #使用发送方的公钥来验证发送方的签名的开始
        h = SHA.new(str(transaction).encode('utf8'))
        return verifier.verify(h, binascii.unhexlify(signature)) #验证发送方的签名 #误报错误,可以直接运行

    def submit_transaction(self, sender_address, recipient_address, value, signature):
        """
        Add a transaction to transactions array if the signature verified
        """
        transaction = OrderedDict({'sender_address': sender_address, 
                                    'recipient_address': recipient_address,
                                    'value': value})

        #Reward for mining a block
        if sender_address == MINING_SENDER:
            self.transactions.append(transaction)
            return len(self.chain) + 1
        #Manages transactions from wallet to another wallet
        else:
            transaction_verification = self.verify_transaction_signature(sender_address, signature, transaction)
            if transaction_verification:
                self.transactions.append(transaction)
                return len(self.chain) + 1
            else:
                return False

    def create_block(self, nonce, previous_hash):
        """
        Add a block of transactions to the blockchain
        """
        block = {'block_number': len(self.chain) + 1,
                'timestamp': time(),
                'transactions': self.transactions,
                'nonce': nonce,
                'previous_hash': previous_hash}

        # Reset the current list of transactions
        self.transactions = []

        self.chain.append(block)
        return block

    def hash(self, block):
        """
        Create a SHA-256 hash of a block
        """
        # We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes
        block_string = json.dumps(block, sort_keys=True).encode()

        return hashlib.sha256(block_string).hexdigest()

    def proof_of_work(self):
        """
        Proof of work algorithm
        """
        last_block = self.chain[-1]
        last_hash = self.hash(last_block)

        nonce = 0
        while self.valid_proof(self.transactions, last_hash, nonce) is False:
            nonce += 1

        return nonce

    def valid_proof(self, transactions, last_hash, nonce, difficulty=MINING_DIFFICULTY):
        """
        Check if a hash value satisfies the mining conditions. This function is used within the proof_of_work function.
        """
        guess = (str(transactions)+str(last_hash)+str(nonce)).encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        return guess_hash[:difficulty] == '0'*difficulty

    def valid_chain(self, chain):
        """
        check if a bockchain is valid
        """
        last_block = chain[0]
        current_index = 1

        while current_index < len(chain):
            block = chain[current_index]
            #print(last_block)
            #print(block)
            #print("\n-----------\n")
            # Check that the hash of the block is correct
            if block['previous_hash'] != self.hash(last_block):
                return False

            # Check that the Proof of Work is correct
            #Delete the reward transaction
            transactions = block['transactions'][:-1]
            # Need to make sure that the dictionary is ordered. Otherwise we'll get a different hash
            transaction_elements = ['sender_address', 'recipient_address', 'value']
            transactions = [OrderedDict((k, transaction[k]) for k in transaction_elements) for transaction in transactions]

            if not self.valid_proof(transactions, block['previous_hash'], block['nonce'], MINING_DIFFICULTY):
                return False

            last_block = block
            current_index += 1

        return True

    def resolve_conflicts(self):
        """
        Resolve conflicts between blockchain's nodes
        by replacing our chain with the longest one in the network.
        """
        neighbours = self.nodes
        new_chain = None

        # We're only looking for chains longer than ours
        max_length = len(self.chain)

        # Grab and verify the chains from all the nodes in our network
        for node in neighbours:
            print('http://' + node + '/chain')
            response = requests.get('http://' + node + '/chain')

            if response.status_code == 200:
                length = response.json()['length']
                chain = response.json()['chain']

                # Check if the length is longer and the chain is valid
                if length > max_length and self.valid_chain(chain):
                    max_length = length
                    new_chain = chain

        # Replace our chain if we discovered a new, valid chain longer than ours
        if new_chain:
            self.chain = new_chain
            return True

        return False

# Instantiate the Node
app = Flask(__name__)
CORS(app)

# Instantiate the Blockchain
blockchain = Blockchain()

@app.route('/')
def index():
    return render_template('./index.html')

@app.route('/configure')
def configure():
    return render_template('./configure.html')


@app.route('/transactions/new', methods=['POST'])
def new_transaction():
    values = request.form

    # Check that the required fields are in the POST'ed data
    required = ['sender_address', 'recipient_address', 'amount', 'signature']
    if not all(k in values for k in required):
        return 'Missing values', 400
    # Create a new Transaction
    transaction_result = blockchain.submit_transaction(values['sender_address'], values['recipient_address'], values['amount'], values['signature'])

    if transaction_result == False:
        response = {'message': 'Invalid Transaction!'}
        return jsonify(response), 406
    else:
        response = {'message': 'Transaction will be added to Block '+ str(transaction_result)}
        return jsonify(response), 201

@app.route('/transactions/get', methods=['GET'])
def get_transactions():
    #Get transactions from transactions pool
    transactions = blockchain.transactions

    response = {'transactions': transactions}
    return jsonify(response), 200

@app.route('/chain', methods=['GET'])
def full_chain():
    response = {
        'chain': blockchain.chain,
        'length': len(blockchain.chain),
    }
    return jsonify(response), 200

@app.route('/mine', methods=['GET'])
def mine():
    # We run the proof of work algorithm to get the next proof...
    last_block = blockchain.chain[-1]
    nonce = blockchain.proof_of_work()

    # We must receive a reward for finding the proof.
    blockchain.submit_transaction(sender_address=MINING_SENDER, recipient_address=blockchain.node_id, value=MINING_REWARD, signature="")

    # Forge the new Block by adding it to the chain
    previous_hash = blockchain.hash(last_block)
    block = blockchain.create_block(nonce, previous_hash)

    response = {
        'message': "New Block Forged",
        'block_number': block['block_number'],
        'transactions': block['transactions'],
        'nonce': block['nonce'],
        'previous_hash': block['previous_hash'],
    }
    return jsonify(response), 200


@app.route('/nodes/register', methods=['POST'])
def register_nodes():
    values = request.form
    nodes = values.get('nodes').replace(" ", "").split(',')

    if nodes is None:
        return "Error: Please supply a valid list of nodes", 400

    for node in nodes:
        blockchain.register_node(node)

    response = {
        'message': 'New nodes have been added',
        'total_nodes': [node for node in blockchain.nodes],
    }
    return jsonify(response), 201

@app.route('/nodes/resolve', methods=['GET'])
def consensus():
    replaced = blockchain.resolve_conflicts()

    if replaced:
        response = {
            'message': 'Our chain was replaced',
            'new_chain': blockchain.chain
        }
    else:
        response = {
            'message': 'Our chain is authoritative',
            'chain': blockchain.chain
        }
    return jsonify(response), 200

@app.route('/nodes/get', methods=['GET'])
def get_nodes():
    nodes = list(blockchain.nodes)
    response = {'nodes': nodes}
    return jsonify(response), 200


if __name__ == '__main__':
    from argparse import ArgumentParser

    parser = ArgumentParser()
    parser.add_argument('-p', '--port', default=5000, type=int, help='port to listen on')
    args = parser.parse_args()
    port = args.port

    app.run(host='127.0.0.1', port=port)

【学习后记】
一、区块链的实现没有想象中的复杂
今天虽然只是分析别人现存的代码来验证实现一个功能简单的区块链应用,却还是被区块链原理简单,实现并不复杂的特性所再次震撼。
只是对过去密码学的简单使用,加上众多矿工节点的参与,就可以实现如此伟大的创举。

原文地址:http://www.941xue.com/content.aspx?id=257

【欢迎大家加入[就是要学]社群】
如今,这个世界的变化与科技的发展就像一个机器猛兽,它跑得越来越快,跑得越来越快,在我们身后追赶着我们。
很多人很早就放弃了成长,也就放弃了继续奔跑,多数人保持终身不变的样子,原地不动,成为那猛兽的肚中餐——当然那也不错,在猛兽的逼迫下,机械的重复着自我感觉还良好地稳定工作与生活——而且多半感觉不到这有什么不正常的地方,因为在猛兽肚子里的是大多数人,就好像大多数人都在一个大坑里,也就感觉不出来这是一个大坑了,反而坑外的世界显得有些不大正常。
为什么我们不要做坑里的大多数人?
因为真正的人生,应当有百万种可能 ;因为真正的一生可以有好多辈子组成,每一辈子都可以做自己喜欢的事情;因为真正的人生,应当有无数种可以选择的权利,而不是总觉得自己别无选择。因为我们要成为一九法则中为数不多的那个一;因为我们要成为自己人生的导演而不是被迫成为别人安排的戏目中的演员。
【请注意】
就是要学社群并不会告诉你怎样一夜暴富!也不会告诉你怎样不经努力就实现梦想!
【请注意】
就是要学社群并没有任何可以应付未来一切变化的独门绝技,也没有值得吹嘘的所谓价值连城的成功学方法论!
【请注意】
社群只会互相帮助,让每个人都看清自己在哪儿,自己是怎样的,重新看见心中的梦想,唤醒各自内心中的那个英雄,然后勇往直前,成为自己想要成为的样子!
期待与你并肩奔赴未来!
www.941xue.com
QQ群:646854445 (【就是要学】终身成长)

【同步语音笔记】
https://www.ximalaya.com/keji/19103006/255591770

【学习过程屏幕录屏】
https://www.bilibili.com/video/av89487090/

链接:https://pan.baidu.com/s/1lj0NDURXZToqzayJcqYVXQ
提取码:z0tl

  • 暂无回复。