孤荷凌寒自学python第九十三天认识区块链007

【主要内容】

今天开始初步分析从github上获取的开源代码怎么实现简单区块链的入门知识,共用时间30分钟。

(此外整理作笔记花费了约70分钟)

详细学习过程见文末学习过程屏幕录像。

完全使用别人的代码完整的实践了一次信息在区块链上的发送接收,信息的加密与签名及验签,同时实践了一下生成区块与将交易信息写入区块(原理还不懂)。

主要通过以下网络资源进行学习:

http://docs.jinkan.org/docs/flask/quickstart.html

密码学部分参看了以下文章:

https://www.jb51.net/article/86022.htm

 

【学习笔记】

一、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)



```

【学习后记】

一、区块链的实现没有想象中的复杂

今天虽然只是分析别人现存的代码来验证实现一个功能简单的区块链应用,却还是被区块链原理简单,实现并不复杂的特性所再次震撼。

只是对过去密码学的简单使用,加上众多矿工节点的参与,就可以实现如此伟大的创举。

 

【关于坚持自学的例行说明】

最后例行说明下,我为什么要坚持自学。

一、为什么一把年纪还在学习

放弃很多去聚餐,去HI歌,去游玩,去看电影,去追剧……的时间,然后进行着这个年纪似乎已不应当再进行的学习,引来身边人们无尽的不解与鄙夷甚至可怜……

但我不想放弃终身学习的誓言。

因为——

我对我今天的生活现状并不认同!

罗伯特清崎告诉过我们,反省自己当下的生活是不是自己想要的,这难道不是最好的动力与答案?

走过了大半生,然后才发现曾经、当下所正在进行的人生并不是自己想要的,那是一种怎样的体验?

只有心中真切的感受才能回答这个问题,而任凭再丰富的语言也是无法描绘出来的。

经历半生的跋涉,却发现走得并不正确,有多少人有勇气承认自己过去的一切都是错误的呢?

而我愿意告诉过去的我:“你错了!”

那么已经历半生错误,年岁之大又压于头顶,还有希望从这架的梯子的半端重新爬下,再蹒跚着爬上另一架梯子吗?

我宁愿相信还有希望!

这便是我为什么要继续坚持终身学习下去的全部理由。

二、这个年纪还在学这些技术有意义吗

纯的技术对这把年纪其实已没有意义。

但兴趣可以超越意义。

但技术可以引来思想的变革,这才是意义。

投资自己的头脑 ,改革自己的思想,这是最保值,更长远的投资,过去我从来没有投资过,错过太多,那就从投资自己头脑开始吧。

罗伯特清崎告诉我们,真正的富有是时间的富有;真正的自由是可以决定自己愿意做什么的自由。

因为我愿意做我兴趣所在的事,所以我希望我有自由选择的那一天,虽然今天离那一天可能还是那么遥远,但我愿意相信,每天多赶几步,离希望就更近一步。

再者,虽然我可能再已无法完全完整的掌握这些技术了,但技术本身却可以启迪心的觉醒,激发灵感,那么只要多了解一点,我相信我将离那个正离我而去跑得越来越快的未来更近一点,不至于被未知的那个未来抛弃得太远。

于是我怎能放弃追逐求索的步伐?

我要坚信:感觉太迟的时候,也许还不算太迟。

 

感谢一直以来关注我,鼓励我的你!

若不嫌弃这一个到了高龄才长大的可笑可叹的我,请不吝赐教。

我的q号是:578652607,敬候你的指点。

 

 

【同步语音笔记】

https://www.ximalaya.com/keji/19103006/255591770

 

【学习过程屏幕录屏】

https://www.bilibili.com/video/av89487090/

 

链接:https://pan.baidu.com/s/1lj0NDURXZToqzayJcqYVXQ

提取码:z0tl

复制这段内容后打开百度网盘手机App,操作更方便哦

 

猜你喜欢

转载自www.cnblogs.com/lhghroom/p/12324383.html
今日推荐