zoukankan      html  css  js  c++  java
  • 孤荷凌寒自学python第九十五天认识区块链009

    【主要内容】

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

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

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

    今天完成了【blockchain.py】文件源代码的部分细节代码的学习分析,继续添加了更详细的批注,对实现原理有了比较全面的理解。

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

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

    新补充学习了一个Python的基础知识: OrderedDict类

    参照学习的网络博客内容如下:

    http://www.cnblogs.com/gide/p/6370082.html

     

    【学习笔记】

    一、对【blockchain.py】文件的理解批注第二天

    今天的学习笔记都作到了注释文本中(学习分析的思维过程可见我的屏幕录像):

    今天主要是从全局实现过程进行研读批注,每个方法函数的内部细节还没有细致思考。

    下面是到今天为止已对【blockchain.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('-', '') #此测试区块链网络中,此节点的Id标识 。

            #在这个类初始化的方法中,就创建了【创世区块】,且成为本区块链网络的第一个加入的区块(下一行代码完成)

            self.create_block(0, '00') #创世区块诞生

     

        #--添加一个区块链网络中新发现的一个节点到已知节点集合中。

        def register_node(self, node_url):

            """

            添加一个区块链网络中新发现的一个节点到已知节点集合中。

            """

            #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')

     

        #--矿工检查发起一次交易广播的发送者提供的私钥签名是否与它自己的公钥(sender_address)签名的交易相对应。

        def verify_transaction_signature(self, sender_address, signature, transaction):

            """

            矿工检查发起一次交易广播的发送者提供的私钥签名是否与它自己的公钥(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)) #验证发送方的签名 #误报错误,可以直接运行

     

        #----如果verify_transaction_signature方法已验证发起一次交易广播的发送者提供的私钥签名合法,则将此次交易添加到待完成交易列表中。(此交易将等待写入下一次新产生的一个区块中)

        def submit_transaction(self, sender_address, recipient_address, value, signature):

            """

            如果verify_transaction_signature方法已验证发起一次交易广播的发送者提供的私钥签名合法,则将此次交易添加到待完成交易列表中。(此交易将等待写入下一次新产生的一个区块中)

            """

            #OrderedDict类来自于模块:collections

            #OrderedDict用于对字典对象中的元素进行排序,OrderedDict会根据放入元素的先后顺序进行排序,也可以通过sort进行指定元素信息的排序

            transaction = OrderedDict({'sender_address': sender_address,

                                        'recipient_address': recipient_address,

                                        'value': value})

     

            if sender_address == MINING_SENDER:

                #如果当前交易内容是对矿工的挖矿奖励,那么——

                #--将此交易信息添加到待处理(等待写入下一下新创建的区块中)交易信息列表(变量是:transactions)中-----

                self.transactions.append(transaction)

                return len(self.chain) + 1

            else:

                #如果当前交易是节点到节点之间的转账交易 ,那么——

                #--下一句代码验证交易的合法性,即通过交易发送方的交易发送方地址(这儿就是公钥)和发送方的私钥签名来验证。

                transaction_verification = self.verify_transaction_signature(sender_address, signature, transaction)

                #--将此交易信息添加到待处理(等待写入下一下新创建的区块中)交易信息列表(变量是:transactions)中-----

                if transaction_verification:

                    #---如果验证发送方发起的交易合法,那么——

                    self.transactions.append(transaction)

                    return len(self.chain) + 1

                else:

                    return False

     

        #--将已经写入交易信息的一个新区块添加到区块链的末尾,其中previous_hash指定了此区块之前的一个区块,因此就链接在其之后。

        def create_block(self, nonce, previous_hash):

            """

            将已经写入交易信息的一个新区块添加到区块链的末尾,其中previous_hash指定了此区块之前的一个区块,因此就链接在其之后。

            """

     

            #在这个新的区块中,包含了以下信息:

            #'block_number':当前区块编号,这儿就是区块链的顺序号,即链上的第几块区块

            #'timestamp':生成此块(应当是将交易信息写入此块)的时间戳

            #'transactions':所有写入到当前区块的交易信息

            #'nonce':矿工通过算力证明(工作量证明)成功得到的Number Once值,证明其合法创建了一个区块(当前区块)

            #'previous_hash':在当前区块添加到区块链之前,区块链原来的最后一个区块的哈希值。(此值表明了当前区块的上一区块的位置,直到定位连接的作用)

            block = {'block_number': len(self.chain) + 1,

                    'timestamp': time(),

                    'transactions': self.transactions,

                    'nonce': nonce,

                    'previous_hash': previous_hash}

     

            # Reset the current list of transactions

            #因为已经将待处理(等待写入下一下新创建的区块中)交易信息列表(变量是: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()

     

        #此方法通过算法获取一个Number Once值,以通过工作量证明得到生成一个新区块的权限,返回这个Number Once值

        def proof_of_work(self):

            """

            Proof of work algorithm

            此方法通过算法获取一个Number Once值,以通过工作量证明得到生成一个新区块的权限,返回这个Number Once值

            """

            last_block = self.chain[-1] #取出区块链现在的最后一个区块

            last_hash = self.hash(last_block) #取出这最后 一个区块的哈希值(散列值)

     

            #下面通过循环来使Number Once的值从0开始每次增加1来进行尝试,直到得到一个符合算法要求 的Number Once值为止

            nonce = 0

            while self.valid_proof(self.transactions, last_hash, nonce) is False:

                nonce += 1

     

            return nonce #返回这个符合算法要求的Number Once值。

     

        #此函数是上一个方法函数的附属部分,用于检查哈希值是否满足挖掘条件。此函数用于工作函数的证明中。

        def valid_proof(self, transactions, last_hash, nonce, difficulty=MINING_DIFFICULTY):

            """

            检查哈希值是否满足挖掘条件。此函数用于工作函数的证明中。

            """

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

                # 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] #---当前区块链中最长链的最后一个区块,blockchain指当前测试的区块链网络本身,是由类blockchain实例化而得到的对象。

        nonce = blockchain.proof_of_work() #---取得了一个可以实现优先创建(挖出)下一个区块的工作量证明的 Number Once值。

     

        # 由于当前去检查发现的发布广播的交易发起者的一次交易完成,且成功通过工作量算法证明,成功创建(挖出)了一个新区块

        # 则此矿工将获得奖励,下面确认的等待写入新区块的交易信息,就是这个奖励交易(就是直接给此矿工一笔数字代币)的信息,详见下面方法的定义位置。

        blockchain.submit_transaction(sender_address=MINING_SENDER, recipient_address=blockchain.node_id, value=MINING_REWARD, signature="")

                                #我理解为是区块链本身作为发送方, #此交易接收方正是当前节点本身就使用node_id,#此参数是奖励的数字代币金额,#最后一个参数是发起交易方的私钥签名,由于发起交易方是区块链本身,因此签名为空(个人理解 )

        # Forge the new Block by adding it to the chain

        previous_hash = blockchain.hash(last_block) #取出当前区块链中最长链的最后一个区块的Hash值,用作要新加入区块的前导HASH(用于连接)

        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)



    ```

    【学习后记】

    一、对技术的了解,可以加深对新事物的爱好

    听说区块链技术已经好多年了,但一直对其不明所以,于是心思上就总对其半信半疑,没有完全的了解,只是看一些大佬的吹嘘,恐吓,弄得自己对区块链更是不明所以。也听说过虚拟数字货币,据说弄得像血腥的战场,时不时一地鸡毛,就更觉得这玩意是妖怪一般可怕了。

    然而经过这几天对一个简单的Python实现的区块链程序的剖析,在逐渐了解其核心的技术概念后,发现自然有它的生命力所在,于是更加深了对新事物的爱好。

    于是感慨当对新生事物不明所以时,最好是深入地去了解清楚。

     

    为了追赶未来,终身学习,终身进步,我创建了【就是要学 终身成长】社群,欢迎立志于终身学习,终身成长的朋友们加入,共同交流学习。Qq群号码:646854445

    或访问:www.941xue.com

     

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

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

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

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

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

    因为——

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

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

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

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

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

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

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

    我宁愿相信还有希望!

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

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

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

    但兴趣可以超越意义。

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

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

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

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

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

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

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

     

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

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

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

     

    为了追赶未来,终身学习,终身进步,我创建了【就是要学 终身成长】社群,欢迎立志于终身学习,终身成长的朋友们加入,共同交流学习。Qq群号码:646854445

    或访问:www.941xue.com

     

    【同步语音笔记】

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

     

    【学习过程屏幕录屏】

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

    链接:https://pan.baidu.com/s/11_cbpR1GiHUJMVfyiUduWg

    提取码:9g4d

     

    欢迎大家添加我为好友: QQ: 578652607
  • 相关阅读:
    SpringMVC中静态获取request对象 Spring中获取 HttpServletRequest对象【转载】
    springcloud 的loadbalancer 轮询算法切换方法 2021.4.3
    springboot项目启动增加图标
    rabbitmq 端口作用以及修改方法
    centos8 安装rabbitmq
    springcloud config client Value获取不到信息的问题的处理方法
    springcloud config配置git作为数据源然后启动报错 If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
    Sublime Text的列模式如何操作
    centos8 安装redis
    jQuery简单的Ajax调用
  • 原文地址:https://www.cnblogs.com/lhghroom/p/12333412.html
Copyright © 2011-2022 走看看