21026 large

012 孤荷凌寒从零开始学区块链第 12 天继续完成代码的批注理解

941xue · 于 发布 · 90 次阅读

孤荷凌寒自学python第九十八天认识区块链012
【主要内容】
今天继续分析从github上获取的开源代码怎么实现简单区块链的入门知识,共用时间47分钟。
(此外整理作笔记花费了约60分钟)
详细学习过程见文末学习过程屏幕录像。
今天进一步完成了【blockchain.py】文件源代码的部分细节代码的学习分析,继续添加了更详细的批注,今天先是复习了信息加密的历史与现状,然后发现目前正在研究学习的从github上获得的开源代码中,没有对要发送的信息进行使用接收方公钥进行加密,发现只使用了发送方的签名处理,同时对一个区块的内容转换为唯一的hash值的算法部分进行了深入研究。
相关资料通过百度实时搜索学习。相关链接在源代码详细注释中有引用,感谢这些博主的分享。

【学习笔记】
一、对模块:hashlib 的初步理解和认识
博文:https://www.cnblogs.com/wang-yc/p/5616663.html对此有比较好的简单说明
(以下内容摘录自此博文中的部分)
Python3之hashlib
  用于加密相关的操作,代替了md5模块和sha模块,主要提供SHA1,SHA224,SHA256,SHA384,SHA512,MD5算法。
在python3中已经废弃了md5和sha模块,简单说明下md5和sha的使用。
  什么是摘要算法呢?
  摘要算法又称为哈希算法,散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)用于加密相关的操作。

由此我理解到哈希运算的过程中就是进行摘要:
目前我已知有两种摘要算法:
博文:https://www.cnblogs.com/yrxns/p/7727471.html 有相关简单说明
hash.digest()
返回摘要,作为二进制数据字符串值,
二进制结果如:b'\x0c\xc1u\xb9\xc0\xf1\xb6\xa81\xc3\x99\xe2iw&a'

hash.hexdigest()
返回摘要,作为十六进制数据字符串值,
十六进制结果如:0cc175b9c0f1b6a831c399e269772661
这里就把加密后的hash值作为十六进制字符串返回。
据我了解,多数区块链项目都使用的是十六进制。

二、RSA算法的相关认识
(一)来自文章:http://mini.eastday.com/mobile/180122111943090.html
下面是文章中对RSA算法核心的描述:
RSA算法是这样工作的
第一步,随机选择两个不相等的质数p和q。
第二步,计算p和q的乘积n。n的长度就是密钥长度,一般以二进制表示,一般长度是2048位。位数越长,则越难破解。
第三步,计算n的欧拉函数φ(n)。
第四步,随机选择一个整数e,其中是1< e < φ(n),且e与φ(n) 互质。
第五步,计算e对于φ(n)的模反元素d。所谓“模反元素”就是指有一个整数d,可以使得ed被φ(n)除的余数为1。
第六步,将n和e封装成公钥 (n,e) ,n和d封装成私钥 (n,d) 。
假设用户A要向用户B发送加密信息m,他要用公钥 (n,e) 对m进行加密。加密过程实际上是算一个式子:
用户B收到信息c以后,就用私钥 (n, d) 进行解密。解密过程也是算一个式子:
这样用户B知道了用户A发的信息是m。
用户B只要保管好数字d不公开,别人将无法根据传递的信息c得到加密信息m。
RSA算法以(n, e)作为公钥,那么有无可能在已知n和e的情况下,推导出d?
(1)ed≡1 (mod φ(n))。只有知道e和φ(n),才能算出d。
(2)φ(n)=(p-1)(q-1)。只有知道p和q,才能算出φ(n)。
(3)n=pq。只有将n因数分解,才能算出p和q。
所以,如果n可以被很简单地分解,则很容易算出d,意味着信息被破解。
但是目前大整数的因式分解,是一件非常困难的事情。目前,除了暴力破解,还没有发现别的有效方法。也就是说,只要密钥长度足够长,用RSA加密的信息实际上是不能被解破的。
(二)欧拉函数
因为我没有学过高等数学,数学大部分都荒废了,因此特地了解了一下这个函数
其实也不算完全理解 ,不过基本明白了:
φ(n)
求出的就是从1开始(包含1)到n为止(不包含n)中所有的互质数的个数。
比如:
φ(8)=4
因为在大于等于1,小于8(1<=x<8)中的所有数中,x只有四个值符合互质数特征:
1,3,5,7
所以只有4 个。

三、encode()方法
其实接触encode()方法很久,但明显基础不牢,没有完成理解其用法,今天也只是粗略了解了一下:
以博文:https://www.runoob.com/python/att-string-encode.html 为内容
str.encode([encoding='UTF-8'[,errors='strict']])
可以一个实参都不传递给这个方法函数,默认值如上所描述。
用于对字符串的编码进行转换。

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

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:
            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这个值是什么作用不明白

    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):
        #上一句在检查客户端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)

【学习后记】
今天的学习还是显得比较艰难,虽然对加密算法有了较多的了解了,但其基础的数学实现完全是一头雾水,难以理解。
由此可见,没有进入大学的殿堂对人生的影响是有多么巨大,若时光可以重来,我必定不惜一切代价要进入大学课堂去看看!

github: https://github.com/lhghroom/Self-learning-blockchain-from-scratch
原文地址:http://www.941xue.com/content.aspx?id=1299

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

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

【学习过程屏幕录屏】
https://www.bilibili.com/video/av90676090/
链接:https://pan.baidu.com/s/1ZRMe2J5urdwIoPhB-ZPyjA
提取码:rfhk

  • 暂无回复。