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

    【主要内容】

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

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

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

    今天进一步完成了【blockchain.py】文件源代码的部分细节代码的学习分析,继续添加了更详细的批注,今天先是解决了昨天没有理解的一行代码,然后着重理解了检查一个条链是否合法的方法函数的每一行代码。

     

     

    【学习笔记】

    一、对【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

    '''

    #关于RSA模块的详细说明博文:https://www.jianshu.com/p/7d79562717b3

    (下面是这篇博文的开头部分:)

    Crypto 算法库在 python 中最初叫 pycrypto,这个作者有点懒,好几年没有更新,后来就有大佬写了个替代库 pycryptodome。这个库目前只支持 python3,安装也很简单pip install就行了!

    详细的用法可以看看 官方文档(https://www.pycryptodome.org/en/latest/src/api.html)

     

    常见对称密码在 Crypto.Cipher 库下,主要有:DES 3DES AES RC4 Salsa20

    非对称密码在 Crypto.PublicKey 库下,主要有:RSA ECC DSA

    哈希密码在 Crypto.Hash 库下,常用的有:MD5 SHA-1 SHA-128 SHA-256

    随机数在 Crypto.Random 库下

    实用小工具在 Crypto.Util 库下

    数字签名在 Crypto.Signature 库下

    #----------------------

    这篇博文对加密-解密,签名-验签的过程讲得很详细

    https://www.cnblogs.com/pcheng/p/9629621.html

    #注意点:

    RSA加密对明文的长度有所限制,规定需加密的明文最大长度=密钥长度-11(单位是字节,即byte),

    所以在加密和解密的过程中需要分块进行。而密钥默认是1024位,即1024位/8位-11=128-11=117字节。

    所以默认加密前的明文最大长度117字节,解密密文最大长度为128字。那么为啥两者相差11字节呢?

    是因为RSA加密使用到了填充模式(padding),即内容不足117字节时会自动填满,

    用到填充模式自然会占用一定的字节,而且这部分字节也是参与加密的。

    '''

    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 #表示工作量证明算力要求中的——得到的这个十六进制表示的字符串的前MINING_DIFFICULTY位([0:MINING_DIFFICULTY]切片)应当是全是'0'字符这个条件要求。



    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') #创世区块诞生

     

        #--添加一个区块链网络中新发现的一个节点到已知节点集合中。(添加的节点的信息是节点的url信息中的核心部分,即纯域名部分或相对路径)

        def register_node(self, node_url):

            """

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

            """

            #Checking node_url has valid format

            #检查节点的格式,通过urlparse方法将这个节点的url分割成六个部分

            #--下面是对urlparse的研究结论:

            '''

            #来自模块:from urllib.parse import urlparse

            urlparse方法的效果

            将给定的url分解为以下五部分:

            [0]:'scheme'

            [1]:'netloc'

            [2]:'path'

            [3]:'query'

            [4]:'fragment'

            一、

            http://www.baidu.com/m/

            分解信息与返回的五个部分:

     

            fragment:''

            hostname:'www.baidu.com'

            netloc:'www.baidu.com'

            password:None

            path:'/m/'

            port:None

            query:'a=21&b=23'

            scheme:'http'

            username:None

            [0]:'http'

            [1]:'www.baidu.com'

            [2]:'/m/'

            [3]:'a=21&b=23'

            [4]:''

     

            二、

            http:8080//www.baidu.com/m/

            分解信息与返回的五个部分:

     

            fragment:''

            hostname:None

            netloc:''

            password:None

            path:'8080//www.baidu.com/m/'

            port:None

            query:'a=21&b=23'

            scheme:'http'

            username:None

            [0]:'http'

            [1]:''

            [2]:'8080//www.baidu.com/m/'

            [3]:'a=21&b=23'

            [4]:''

     

            三、

            ftp://username:pass@www.baidu.com/m/

            分解信息与返回的五个部分:

     

            fragment:''

            hostname:'www.baidu.com'

            netloc:'username:pass@www.baidu.com'

            password:'pass'

            path:'/m/'

            port:None

            query:'a=21&b=23'

            scheme:'ftp'

            username:'username'

            [0]:'ftp'

            [1]:'username:pass@www.baidu.com'

            [2]:'/m/'

            [3]:'a=21&b=23'

            [4]:''

            '''

            parsed_url = urlparse(node_url)

            if parsed_url.netloc: #如果网络地址不为空,那么就添加没有http://之类修饰的纯的地址,如:www.baidu.com

                self.nodes.add(parsed_url.netloc)

            elif parsed_url.path: #如果网络地址为空,那么就添加相对Url的路径

                # Accepts an URL without scheme like '192.168.0.5:5000'.

                self.nodes.add(parsed_url.path)

            else:

                raise ValueError('Invalid URL') #说明这是一个非标准的Url

     

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

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

            """

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

            """

            public_key = RSA.importKey(binascii.unhexlify(sender_address)) #获取发送方的公钥

            #binascii模块用于二进制与ASCII编码的相互转换,unhexlify方法的作用是:(https://www.cnblogs.com/lyhabc/p/7995254.html)

            #unhexlify方法还原用十六进制表示的二进制数据(即是说返回结果为一个字符串,这里究竟是字符串,还是二进制的byte流?)。需要一个参数:hexstr必须包含偶数个十六进制数字(大写或小写),这儿其实是发送方的公钥的十六进制byte字节。

            #RSA.importKey方法导入标准格式编码的RSA密钥(公共或私有半密钥【什么叫私有半密钥,这是机器 翻译的。】)。

            #---这篇博文似乎作了详尽说明:https://www.jianshu.com/p/6a39610122fa

            #---------------------------------------------------------------

            #使用发送方的公钥来验证发送方的签名的开始

            verifier = PKCS1_v1_5.new(public_key)  #--这儿使用了Crypto.Signature子库中的一种填充方法:PKCS1_v1_5(另有一种填充方法:PKCS1_OAEP)

            #通过PKCS1_v1_5.new方法将发送者的公钥填充为(资料显示为签名或验签,此处是签名还是验签呢?)

            h = SHA.new(str(transaction).encode('utf8'))

            '''

            #h = SHA.new(str(transaction).encode('utf8'))的理解:

            transaction是当前矿工要验证的一次交易的交易信息本身(是一个字典)

            encode('utf8')表示将这个交易信息中的所有字符都转换为utf-8编码

            SHA.new方法使用这个信息(是不是已经被转换成了一个符串?)来算出一个新的HASH值。

            '''

            return verifier.verify(h, binascii.unhexlify(signature)) #验证发送方的签名 #误报错误,可以直接运行

           

            '''

            上一行,使用发送方的公钥处理后的对象verifier(对公钥作了什么处理,我还没有理解)

            通过verify方法对信息内容得到的新hash值 h 与发送者的 私钥 签名 字节串 signature

            进行 签名合法性验证,以验证交易信息transaction的发送方的确是公钥sender_address或public_key拥有者

            '''

     

        #----如果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

            #我们必须确保字典是有序的,否则我们会有不一致的哈希值,下一行代码中,sort_keys=True指明了要进行排序 。

            block_string = json.dumps(block, sort_keys=True).encode()

            '''

            解析:json.dumps(block, sort_keys=True).encode()

            首先通过json.dumps方法将一个区块打散,并进行排序(保证每一次对于同一个区块都是同样的排序)

            这个时候区块被转换成了一个json字符串(不知道怎么描述)

            然后,通过json字符串的encode()方法进行编码处理。

            其中encode方法有两个可选形参,第一个是编码描述字符串,另一个是预定义错误信息

            默认情况下,编码描述字符串参数就是:默认编码为 'utf-8'。此处就是默认编码为'utf-8'

            字符串编码常用类型有:utf-8,gb2312,cp936,gbk等。

            '''

           

            return hashlib.sha256(block_string).hexdigest()

     

            '''

            解析hashlib.sha256(block_string).hexdigest()

            hashlib.sha256(block_string) #来自模块:hashlib (用于加密相关的操作,代替了md5模块和sha模块)(参见此博文:https://www.cnblogs.com/wang-yc/p/5616663.html)

            用sha256加密方法对block_string进行加密(其它加密算法还有:SHA1,SHA224,SHA256,SHA384,SHA512,MD5)

            .hexdigest 哈希字符串的 【摘要算法】(因为哈希值的计算过程就是摘要计算过程)

            (具体说明参见博文:https://www.cnblogs.com/yrxns/p/7727471.html)

            摘要的返回结果有以下两种:

            1.hash.digest()

            返回摘要,作为二进制数据字符串值,二进制结果如:b单引号斜杠x0c斜杠xc1u斜杠xb9斜杠xc0斜杠xf1斜杠xb6斜杠xa81斜杠xc3斜杠x99斜杠xe2iw&a单引号

            2.hash.hexdigest()

            返回摘要,作为十六进制数据字符串值,十六进制结果如:0cc175b9c0f1b6a831c399e269772661

            这里就把加密后的hash值作为十六进制字符串返回。

            据我了解,多数区块链项目都使用的是十六进制。

            '''

     

        #此方法通过算法获取一个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:

                #如果得到的Number Once值不符合要求,那么就继续寻找。

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

            '''

            上一行代码解析:

            根据传入的参数nonce(就是要找的那个Number Once值)来进行尝试运算,得到一个转码为utf-8格式的字符串

            '''

            guess_hash = hashlib.sha256(guess).hexdigest()

            '''

            将此字符串(guess)进行sha256方式加密,并转换为十六进制的字符串

            '''

            return guess_hash[:difficulty] == '0'*difficulty

            '''

            变量difficulty表示工作量证明算力要求中的——得到的这个十六进制表示的字符串的前difficulty位([0:difficulty]切片)应当是0这个条件要求。

            '0'*difficulty 就是 difficulty个'0'组成的字符串。

            guess_hash[:difficulty]就是取出 guess_hash 字符串中的前difficulty个字符,以检查这些字符是否都是'0'

            如果符合要求,就返回True,否则 就返回False

            '''

     

        def valid_chain(self, chain):

            """

            check if a bockchain is valid

            检查bockchain是否有效,即检查是否每个区块都合法

            """

            last_block = chain[0]

            #上一行这里取得的是创世区块,意味着必须从头检查整个区块链上从创世区块到链上最后一个区块为止的所有区块的链接关系

            #下面的while循环就是为了检查链上每一个区块与其连接的前一个区块是否合法相关,通过 检查 previous_hash 来判断

            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):

                    #如果发现当前在检查的区块的previous_hash值与它实际连接的前一区块的hash值不同,则证明此链条有问题,终止检查

                    return False

     

                # Check that the Proof of Work is correct

                #检查工作证明是否正确

                #Delete the reward transaction

                #删除奖励交易,下一行代码中,切片时的[:-1]没有包含原列表中的最后一条交易信息,即矿工奖励 的交易信息。

                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]

                '''

                (k, transaction[k]) for k in transaction_elements

                我的理解是,就是按照 ['sender_address', 'recipient_address', 'value']这个列表中指明 的元素顺序将所有交易信息列表强制排序。

                '''

     

                #重新检查当前块的工作量证明得到 的Number Once值的合法性

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

                    #如果重新检查发现这个Number Once值不合法,则证明这个链条是不正确的。

                    return False

     

                last_block = block #让当前区块变成前一个区块,以迭代到一下次循环

                current_index += 1 #让下一个区块变成当前区块的index计数

     

            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):

            #上一句在检查客户端post过来的参数是否包含了必要的内容。(即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():

        #从浏览器客户端获取通过post传递过来的节点信息

        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实例的注册方法将节点添加到节点集合中去

            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)

     

    ```

    【学习后记】

    今天的学习发现每次添加新的区块到区块链条中之前要检查认为最长的那条链的合法性,检查其合法性的方法流程居然时从创世区块开始一块一块的检查,既要检查每一个块与前一场连接是否合法,还要检查每一个区块创建时的工作量证明是否合法,感觉工作量非常大啊。想到要是一个区块链条有上亿个区块,那这样的检查效率简直有点不敢想象的感觉。当然正在学习的这个源代码是一个实验性的代码,我估计真正成熟的区块链代码应当有更好的检查算法吧。

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

    或访问:www.941xue.com

     

     

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

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

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

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

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

    因为——

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

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

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

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

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

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

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

    我宁愿相信还有希望!

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

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

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

    但兴趣可以超越意义。

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

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

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

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

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

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

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

     

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

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

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

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

    或访问:www.941xue.com

     

     

    【同步语音笔记】

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

     

    【学习过程屏幕录屏】

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

     

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

    提取码:l4x5

     

     

     

    欢迎大家添加我为好友: QQ: 578652607
  • 相关阅读:
    【React Native】使用react-native-wechat 进行微信好友、微信朋友圈进行分享
    【React Native】在网页中打开Android应用程序
    【React Native】错误提示: 在React开发中,我们可能经常会遇到这个一个警告: Can't perform a React state update on an unmounted component.
    【Swift】上传图片到腾讯云(生成token,上传)
    【React Native】集成声网Agora语音通讯
    【React Native】在React Native项目中使用rollbar-react-native记录
    【iOS】上传图片到七牛过程(生成Token,上传)
    【React Native】同步获取本地缓存键值对
    React Native之持久化存储(AsyncStorage、react-native-storage)的使用
    【React Native】手写Alert弹窗(单按钮,两个按钮)
  • 原文地址:https://www.cnblogs.com/lhghroom/p/12354628.html
Copyright © 2011-2022 走看看