使用Python 搭建自己的区块链-Python教程

资源魔 33 0

【相干学习保举:python教程】

你能否会以及我同样,对加密数字货泉底层的区块链技巧十分感兴味,特地想理解他们的运转机制。

然而学习区块链技巧并不是好事多磨,我看多了年夜量的视频教程另有各类课程,终极的觉得就是真正可用的实战课程太少。

我喜爱正在理论中学习,尤为喜爱一代码为根底去理解整个工作机制。假如你我同样喜爱这类学习形式,当你学完本教程时,你将会晓得区块链技巧是若何工作的。

写正在开端以前

记住,区块链是一个 不成变的、有序的 被称为块的记载链。它们能够蕴含买卖、文件或任何您喜爱的数据。但首要的是,他们用哈希 一同被链接正在一同。

假如你没有相熟哈希,这里是一个诠释。

该指南的目的是甚么?

你能够难受地浏览以及编写根底的 Python,由于咱们将经过 HTTP 与区块链进行探讨,以是你也要理解 HTTP 的工作原理。

我需求预备甚么?

确定装置了 Python 3.6 + (另有 pip) ,你还需求装置 Flask、 Requests 库:

    pip install Flask==0.12.2 requests==2.18.4

对了, 你还需求一个支持HTTP的客户端, 比方 Postman 或许 cURL,其余也能够。

源码正在哪儿?
能够点击这里

Step 1: 创立一个区块链

关上你最喜爱的文本编纂器或许IDE, 我集体比拟喜爱 PyCharm. 新建一个名为blockchain.py的文件。 咱们将只用这一个文件就能够。然而假如你仍是没有太分明, 你也能够参考 源码.

形容区块链

咱们要创立一个 Blockchain 类 ,他的结构函数创立了一个初始化的空列表(要存储咱们的区块链),而且另外一个存储买卖。上面是咱们这个类的实例:

blockchain.py

class Blockchain(object):
    def __init__(self):
        self.chain = []
        self.current_transactions = []

    def new_block(self):
        # Creates a new Block and adds it to the chain
        pass

    def new_transaction(self):
        # Adds a new transaction to the list of transactions
        pass

    @staticmethod
    def hash(block):
        # Hashes a Block
        pass

    @property
    def last_block(self):
        # Returns the last Block in the chain
        pass

咱们的 Blockchain 类担任治理链式数据,它会存储买卖而且另有增加新的区块到链式数据的Method。让咱们开端裁减更多Method。

块是甚么样的 ?

每一个块都有一个 索引,一个 工夫戳(Unix工夫戳),一个事务列表, 一个 校验(稍后胪陈) 以及 前一个块的散列

上面是一个Block的例子 :

blockchain.py

block = {
    'index': 1,
    'timestamp': 1506057125.900785,
    'transactions': [
        {
            'sender': "8527147fe1f5426f9dd545de4b27ee00",
            'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",
            'amount': 5,
        }
    ],
    'proof': 324984774000,
    'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
}

正在这一点上,一个 区块链 的概念应该是显著的 - 每一个新块都蕴含正在其内的前一个块的 散列 。 这是至关首要的,由于这是 区块链 不成扭转的缘由:假如攻打者损坏 区块链 中较早的块,则一切后续块将蕴含没有正确的哈希值。

这有情理吗? 假如你尚未想通,花点工夫细心考虑一下 - 这是区块链面前的外围理念。

增加买卖到区块

咱们将需求一个增加买卖到区块的形式。咱们的 new_transaction() 办法的责任就是这个, 而且它十分的简略:

blockchain.py

class Blockchain(object):
    ...

    def new_transaction(self, sender, recipient, amount):
        """
        Creates a new transaction to go into the next mined Block
        :param sender: <str> Address of the Sender
        :param recipient: <str> Address of the Recipient
        :param amount: <int> Amount
        :return: <int> The index of the Block that will hold this transaction
        """

        self.current_transactions.append({
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
        })

        return self.last_block['index'] + 1

new_transaction() 办法增加了买卖到列表,它前往了买卖将被增加到的区块的索引---讲开采下一个这对稍后对提交买卖的用户有用。

创立新的区块

当咱们的 Blockchain 被实例化后,咱们需求将 创世 区块(一个不前导区块的区块)增加出来出来。咱们还需求向咱们的来源块增加一个 证实,这是挖矿的后果(或工作证实)。 咱们稍后会具体探讨挖矿。

除了了正在结构函数中创立 创世 区块外,咱们还会补全 new_block()new_transaction() 以及 hash() 函数:

blockchain.py

import hashlib
import json
from time import time

class Blockchain(object):
    def __init__(self):
        self.current_transactions = []
        self.chain = []

        # 创立创世区块
        self.new_block(previous_hash=1, proof=100)

    def new_block(self, proof, previous_hash=None):
        """
        创立一个新的区块到区块链中
        :param proof: <int> 由工作证实算法天生的证实
        :param previous_hash: (Optional) <str> 前一个区块的 hash 值
        :return: <dict> 新区块
        """

        block = {
            'index': len(self.chain) + 1,
            'timestamp': time(),
            'transactions': self.current_transactions,
            'proof': proof,
            'previous_hash': previous_hash or self.hash(self.chain[-1]),
        }

        # 重置以后买卖记载
        self.current_transactions = []

        self.chain.append(block)
        return block

    def new_transaction(self, sender, recipient, amount):
        """
        创立一笔新的买卖到下一个被发掘的区块中
        :param sender: <str> 发送人的地点
        :param recipient: <str> 接纳人的地点
        :param amount: <int> 金额
        :return: <int> 持有本次买卖的区块索引
        """
        self.current_transactions.append({
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
        })

        return self.last_block['index'] + 1

    @property
    def last_block(self):
        return self.chain[-1]

    @staticmethod
    def hash(block):
        """
        给一个区块天生 SHA-256 值
        :param block: <dict> Block
        :return: <str>
        """

        # 咱们必需确保这个字典(区块)是通过排序的,不然咱们将会失去纷歧致的散列
        block_string = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()

下面的代码应该是直白的 --- 为了让代码明晰,我增加了一些正文以及文档阐明。 咱们差没有多实现了咱们的区块链。 但正在这个时分你肯定很纳闷新的块是怎样被创立、铸造或发掘的。

工作量证实算法

应用工作量证实(PoW)算法,来证实是若何正在区块链上创立或发掘新的区块。PoW 的指标是较量争论出一个合乎特定前提的数字,这个数字关于一切人而言必需正在较量争论上十分艰难,但易于验证。这是工作证实面前的外围思维。

咱们将看到一个简略的例子协助你了解:

假定一个整数 x 乘以另外一个整数 y 的积的 Hash 值必需以 0 末端,即 hash(x * y) = ac23dc...0。设 x = 5,求y

用 Python 完成:

from hashlib import sha256
x = 5
y = 0  # We don't know what y should be yet...
while sha256(f'{x*y}'.encode()).hexdigest()[-1] != "0":
    y += 1
print(f'The solution is y = {y}')

后果是:y = 21。由于,天生的 Hash 值末端必需为 0

hash(5 * 21) = 1253e9373e...5e3600155e860

正在比特币中,工作量证实算法被称为 Hashcash ,它以及下面的成绩很类似,只不外较量争论难度十分年夜。这就是矿工们为了抢夺创立区块的权益而争相较量争论的成绩。 通常,较量争论难度与指标字符串需求餍足的特定字符的数目成反比,矿工算出后果后,就会取得肯定数目的比特币处分(经过买卖)。

验证后果,当然十分容易。

完成工作量证实

让咱们来完成一个类似 PoW 算法。规定相似下面的例子:

找到一个数字 P ,使患上它与前一个区块的 Proof 拼接成的字符串的 Hash 值以 4 个零扫尾。

blockchain.py

import hashlib
import json

from time import time
from uuid import uuid4

class Blockchain(object):
    ...

    def proof_of_work(self, last_proof):
        """
        Simple Proof of Work Algorithm:
         - Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'
         - p is the previous proof, and p' is the new proof
        :param last_proof: <int>
        :return: <int>
        """

        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        return proof

    @staticmethod
    def valid_proof(last_proof, proof):
        """
        Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?
        :param last_proof: <int> Previous Proof
        :param proof: <int> Current Proof
        :return: <bool> True if correct, False if not.
        """

        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        return guess_hash[:4] == "0000"

权衡算法复杂度的方法是修正零扫尾的个数。应用 4 个来用于演示,你会发现多一个零城市年夜年夜添加较量争论出后果所需的工夫。

如今 Blockchain 类根本曾经实现了,接上去应用 HTTP Requests 来进行交互。

Step 2: Blockchain 作为 API 接口

咱们将应用 Python Flask 框架,这是一个轻量 Web 使用框架,它不便将网络申请映照到 Python 函数,如今咱们来让 Blockchain 运转正在基于 Flask web 上。

咱们将创立三个接口:

  • /transactions/new 创立一个买卖并增加到区块
  • /mine 通知效劳器去发掘新的区块
  • /chain 前往整个区块链

创立节点

咱们的 Flask 效劳器 将表演区块链网络中的一个节点。咱们先增加一些框架代码:

blockchain.py

import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4

from flask import Flask

class Blockchain(object):
    ...

# Instantiate our Node(实例化咱们的节点)
app = Flask(__name__)

# Generate a globally unique address for this node(为这个节点天生一个寰球惟一的地点)
node_identifier = str(uuid4()).replace('-', '')

# Instantiate the Blockchain(实例化 Blockchain类)
blockchain = Blockchain()

@app.route('/mine', methods=['GET'])
def mine():
    return "We'll mine a new Block"

@app.route('/transactions/new', methods=['POST'])
def new_transaction():
    return "We'll add a new transaction"

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

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

简略的阐明一下以上代码:

  • 第 15 行:实例化节点。浏览更多对于 Flask 内容。
  • 第 18 行:为节点创立一个随机的称号。.
  • 第 21 行:实例化 Blockchain 类。
  • 第 24--26 行:创立 /mine 接口,GET 形式申请。
  • 第 28--30 行:创立 /transactions/new 接口,POST 形式申请,能够给接口发送买卖数据。
  • 第 32--38 行:创立 /chain 接口,前往整个区块链。
  • 第 40--41 行:效劳器运转端口 5000 。

发送买卖

发送到节点的买卖数据构造以下:

{
 "sender": "my address",
 "recipient": "someone else's address",
 "amount": 5
}

由于咱们曾经有了增加买卖的办法,以是基于接口来增加买卖就很简略了。让咱们为增加事务写函数:

blockchain.py

import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4

from flask import Flask, jsonify, request

...

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

    # Check that the required fields are in the POST'ed data
    required = ['sender', 'recipient', 'amount']
    if not all(k in values for k in required):
        return 'Missing values', 400

    # Create a new Transaction
    index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])

    response = {'message': f'Transaction will be added to Block {index}'}
    return jsonify(response), 201

挖矿

挖矿恰是神秘所正在,它很简略,做了一下三件事:

  1. 较量争论工作量证实 PoW
  2. 经过新增一个买卖授予矿工(本人)一个币
  3. 结构新区块并将其增加到链中

blockchain.py

import hashlib
import json

from time import time
from uuid import uuid4

from flask import Flask, jsonify, request

...

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

    # We must receive a reward for finding the proof.
    # The sender is "0" to signify that this node has mined a new coin.
    blockchain.new_transaction(
        sender="0",
        recipient=node_identifier,
        amount=1,
    )

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

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

留意买卖的接纳者是咱们本人的效劳器节点,咱们做的年夜局部工作都只是环抱 Blockchain 类办法进行交互。到此,咱们的区块链就算实现了,咱们来实际运转下。

Step 3: 运转区块链

你能够应用 cURL 或 Postman 去以及 API 进行交互

启动 Server:

$ python blockchain.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

让咱们经过申请 http://localhost:5000/mine ( GET )来进行挖矿:

用 Postman 发动一个 GET 申请.

创立一个买卖申请,申请 http://localhost:5000/transactions/new (POST),如图

假如没有是应用 Postman,则用一下的 cURL 语句也是同样的:

$ curl -X POST -H "Content-Type: application/json" -d '{
 "sender": "d4ee26eee15148ee92c6cd394edd974e",
 "recipient": "someone-other-address",
 "amount": 5
}' "http://localhost:5000/transactions/new"

正在挖了两次矿之后,就有 3 个块了,经过申请 http://localhost:5000/chain 能够失去一切的块信息

{
  "chain": [
    {
      "index": 1,
      "previous_hash": 1,
      "proof": 100,
      "timestamp": 1506280650.770839,
      "transactions": []
    },
    {
      "index": 2,
      "previous_hash": "c099bc...bfb7",
      "proof": 35293,
      "timestamp": 1506280664.717925,
      "transactions": [
        {
          "amount": 1,
          "recipient": "8bbcb347e0634905b0cac7955bae152b",
          "sender": "0"
        }
      ]
    },
    {
      "index": 3,
      "previous_hash": "eff91a...10f2",
      "proof": 35089,
      "timestamp": 1506280666.1086972,
      "transactions": [
        {
          "amount": 1,
          "recipient": "8bbcb347e0634905b0cac7955bae152b",
          "sender": "0"
        }
      ]
    }
  ],
  "length": 3
}

Step 4: 分歧性(共鸣)

咱们曾经有了一个根本的区块链能够承受买卖以及挖矿。然而区块链零碎应该是散布式的。既然是散布式的,那末咱们终究拿甚么保障一切节点有一样的链呢?这就是分歧性成绩,咱们要想正在网络上有多个节点,就必需完成一个分歧性的算法。

注册节点

正在完成分歧性算法以前,咱们需求找到一种形式让一个节点晓得它相邻的节点。每一个节点都需求保留一份蕴含网络中其它节点的记载。因而让咱们新增几个接口:

  1. /nodes/register 接纳 URL 方式的新节点列表.
  2. /nodes/resolve 执行分歧性算法,处理任何抵触,确保节点领有正确的链.

咱们修正下 Blockchain 的 init 函数并提供一个注册节点办法:

blockchain.py

...
from urllib.parse import urlparse
...

class Blockchain(object):
    def __init__(self):
        ...
        self.nodes = set()
        ...

    def register_node(self, address):
        """
        Add a new node to the list of nodes
        :param address: <str> Address of node. Eg. 'http://192.168.0.5:5000'
        :return: None
        """

        parsed_url = urlparse(address)
        self.nodes.add(parsed_url.netloc)

咱们用 set 来贮存节点,这是一种防止反复增加节点的简略办法.

完成共鸣算法

就像先前讲的那样,当一个节点与另外一个节点有没有同的链时,就会孕育发生抵触。 为理解决这个成绩,咱们将制订最长的无效链条是最权势巨子的规定。换句话说就是:正在这个网络里最长的链就是最权势巨子的。 咱们将应用这个算法,正在网络中的节点之间告竣共鸣。

blockchain.py

...
import requests

class Blockchain(object)
    ...

    def valid_chain(self, chain):
        """
        Determine if a given blockchain is valid
        :param chain: <list> A blockchain
        :return: <bool> True if valid, False if not
        """

        last_block = chain[0]
        current_index = 1

        while current_index < len(chain):
            block = chain[current_index]
            print(f'{last_block}')
            print(f'{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
            if not self.valid_proof(last_block['proof'], block['proof']):
                return False

            last_block = block
            current_index += 1

        return True

    def resolve_conflicts(self):
        """
        This is our Consensus Algorithm, it resolves conflicts
        by replacing our chain with the longest one in the network.
        :return: <bool> True if our chain was replaced, False if not
        """

        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:
            response = requests.get(f'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

第一个办法 valid_chain() 担任反省一个链能否无效,办法是遍历每一个块并验证散列以及证实。

resolve_conflicts() 是一个遍历咱们一切街坊节点的办法,下载它们的链并应用下面的办法验证它们。 假如找到一个长度年夜于咱们的无效链条,咱们就庖代咱们的链条。

咱们将两个端点注册到咱们的API中,一个用于增加相邻节点,另外一个用于处理抵触:

blockchain.py

@app.route('/nodes/register', methods=['POST'])
def register_nodes():
    values = request.get_json()

    nodes = values.get('nodes')
    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': list(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

正在这一点上,假如你喜爱,你能够应用一台没有同的机械,并正在你的网络上启动没有同的节点。 或许应用同一台机械上的没有同端口启动过程。 我正在我的机械上,没有同的端口上创立了另外一个节点,并将其注册到以后节点。 因而,我有两个节点:http://localhost:5000 以及 http://localhost:5001。 注册一个新节点:

而后我正在节点 2 上发掘了一些新的块,以确保链条更长。 之后,我正在节点1上挪用 GET /nodes/resolve,此中链由分歧性算法庖代:

这是一个包,去找一些冤家一同,以协助测试你的区块链。

我心愿本文能鼓励你发明更多新货色。我之以是对数字货泉出神,是由于我置信区块链会很快扭转咱们对待事物的形式,包罗经济、当局、档案治理等。

更新:我方案正在接上去的第2局部中持续探讨区块链买卖验证机制,并探讨一些能够让区块链进行消费的办法。

相干保举:编程视频课程

以上就是应用Python 搭建本人的区块链的具体内容,更多请存眷资源魔其它相干文章!

标签: Python python教程 python编程 python使用问题 区块链

抱歉,评论功能暂时关闭!