通过python构建一个区块链来学习区块链

栏目: Python · 发布时间: 5年前

内容简介:了解区块链Blockchains如何工作的最快方法就是构建一个区块链。你来到这里是因为,和我一样,你对加密钱币的崛起感到很兴奋。而且你想知道区块链是如何工作的,想了解它们背后的基本技术。但理解区块链并不容易,或者至少不适合我。我在密集的视频中跋涉,接着是千疮百孔的教程,并且处理了极少数放大挫折的例子。我喜欢边做边学。它迫使我在代码级别处理主题,这使得它坚持下去。如果你这样做,在本指南的最后,你将拥有一个功能正常的区块链,可以牢牢掌握它们的工作原理。

了解区块链Blockchains如何工作的最快方法就是构建一个区块链。你来到这里是因为,和我一样,你对加密钱币的崛起感到很兴奋。而且你想知道区块链是如何工作的,想了解它们背后的基本技术。

但理解区块链并不容易,或者至少不适合我。我在密集的视频中跋涉,接着是千疮百孔的教程,并且处理了极少数放大挫折的例子。

我喜欢边做边学。它迫使我在代码级别处理主题,这使得它坚持下去。如果你这样做,在本指南的最后,你将拥有一个功能正常的区块链,可以牢牢掌握它们的工作原理。

在你开始之前……

请记住,区块链是一个名为Blocks的不可变的连续记录链。它们可以包含你喜欢的交易,文件或任何数据。但重要的是他们用哈希链接在一起。

如果你不确定哈希是什么,这里有一个 解释

本指南的目标是谁?你应该舒服地阅读和能编写一些基本的Python,并且对HTTP请求的工作方式有一些了解,因为我们将通过HTTP与我们的区块链交互。

我需要什么?确保安装了 Python 3.6 + (以及 pip )。你还需要安装Flask和精彩的Requests库:

pip install Flask==0.12.2 requests==2.18.4

哦,你还需要一个HTTP客户端,比如 Postman 或cURL。任何一个都可以。

最终的代码在哪里? 源代码 可在此处获得。

第1步:构建区块链

打开你最喜欢的文本编辑器或IDE,我个人 PyCharm 。创建一个名为 blockchain.py 的新文件。我们只会使用一个文件,但是如果你迷路了,你总是可以参考源代码。

代表区块链

我们将创建一个Blockchain类,其构造函数创建一个初始的空列表(用于存储我们的Blockchain链),另一个用于存储交易。这是我们Class的蓝图:

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类负责管理链。它将存储交易并具有一些辅助方法,用于向链中添加新块。让我们开始充实一些方法。

Block看起来像什么?

每个块都有一个索引,一个时间戳(在Unix时间内),一个交易列表 ,一个证明(后面会有更多内容),以及前一个块的哈希值。

以下是单个Block的示例:

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

在这一点上,链的概念应该是显而易见的,每个新块本身包含前一个块的hash。这是至关重要的,因为它是区块链不可变性的原因:如果攻击者破坏链中的早期块,则所有后续块将包含不正确的哈希值。

这有意义吗?如果没有,花一些时间了解,它是区块链背后的核心理念。

将交易添加到块

我们需要一种向块添加交易的方法。我们的 new_transaction() 方法对此负责,而且非常简单:

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被实例化时,我们需要用一个创世块,一个没有前任的区块来种子化它。我们还需要在我们的创世块中添加一个证明 proof ,这是挖掘(或工作量证明)的结果。我们稍后会详细谈论挖掘。

除了在构造函数中创建genesis块之外,我们还将 new_block()new_transaction()hash()

import hashlib
import json
from time import time


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

        # Create the genesis block
        self.new_block(previous_hash=1, proof=100)

    def new_block(self, proof, previous_hash=None):
        """
        Create a new Block in the Blockchain
        :param proof: <int> The proof given by the Proof of Work algorithm
        :param previous_hash: (Optional) <str> Hash of previous Block
        :return: <dict> New Block
        """

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

        # Reset the current list of transactions
        self.current_transactions = []

        self.chain.append(block)
        return block

    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

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

    @staticmethod
    def hash(block):
        """
        Creates a SHA-256 hash of a Block
        :param block: <dict> Block
        :return: <str>
        """

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

以上内容应该是直截了当的,我已经添加了一些注释和文档字符串以帮助保持清晰。我们几乎完成了代表我们的区块链。但在这一点上,你必须想知道如何创建,伪造或挖掘新块。

理解工作量证明

工作量证明算法(PoW)是在区块链上创建或挖掘新块的方式。PoW的目标是发现一个解决问题的数字。这个数字必须很难找到,但很容易通过网络上的任何人进行核算——计算。这是工作证明背后的核心理念。

我们将看一个非常简单的例子来帮助这个陷入困境。

让我们决定某个整数 x 的hash乘以另一个 y 必须以 0 结尾。所以, hash(x * y) = ac23dc...0 。对于这个简化的例子,让我们修复 x = 5 。在 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 。它与我们上面的基本示例没有什么不同。这是矿工为了创造一个新的块而竞争解决的算法。通常,难度取决于字符串中搜索的字符数。 然后矿工通过接收硬币进行交易而获得奖励。

网络能够轻松验证其解决方案。

实施基本的工作量证明

让我们为区块链实现类似的算法。我们的规则与上面的例子类似:

找到一个数字p,当使用前一个块的解决方案进行hash时,会生成 一个带有4个前导 0hash

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就足够了。你会发现,添加一个前导零会使找到解决方案所需的时间产生巨大差异。

我们的类几乎完成了,我们准备开始使用HTTP请求与它进行交互。

第2步:我们的区块链作为API

我们将使用Python Flask Framework。它是一个微框架,可以很容易地将端点映射到Python函数。这允许我们使用HTTP请求通过Web与我们的区块链交互。

我们将创建三种方法:

/transactions/new
/mine
/chain

设置flask

我们的“服务器”将在我们的区块链网络中形成一个单一节点。让我们创建一些样板代码:

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


@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
}

由于我们已经有了用于向块添加交易的类方法,因此其余方法很简单。让我们编写添加交易的函数:

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个硬币的交易来奖励矿工(我们)。
  • 通过将新块添加到链中来构造新块。
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类的方法进行交互。此时,我们已经完成了,并且可以开始与我们的区块链进行交互。

第3步:与我们的区块链交互

你可以使用普通的旧cURL或Postman通过网络与我们的API进行交互。

启动服务器:

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

让我们通过向 http://localhost:5000/mine 发出 GET 请求来尝试挖掘一个块:

通过python构建一个区块链来学习区块链

让我们通过向 http://localhost:5000/transactions/new 发出 POST 请求来创建一个新交易,其中包含我们的交易结构的主体:

通过python构建一个区块链来学习区块链

如果你不使用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
}

第4步:达成共识

这很酷。我们有一个基本的区块链接受交易并允许我们挖掘新的区块。但区块链的重点在于它们应该是去中心化的。如果它们是去中心化的,我们究竟如何确保它们都反映出同一条链?这被称为共识问题,如果我们想在网络中使用多个节点,我们必须实施共识算法。

注册新节点

在我们实现共识算法之前,我们需要一种方法让节点了解网络上的相邻节点。我们网络上的每个节点都应该保留网络上其他节点的注册表。因此,我们需要更多的端点:

/nodes/register
/nodes/resolve

我们需要修改Blockchain的构造函数并提供注册节点的方法,一种向网络添加相邻节点的方法:

...
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() 来保存节点列表。这是一种确保添加新节点是幂等的廉价方法,这意味着无论我们添加特定节点多少次,它都只出现一次。

实施共识算法

如上所述,冲突是指一个节点与另一个节点具有不同的链。要解决此问题,我们将制定最长有效链具有权威性的规则。换句话说,网络上最长的链是事实上的链。使用此算法,我们在网络中的节点之间达成共识。

...
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() 负责通过循环遍历每个块并验证hash和证明来检查链是否有效。

resolve_conflicts() 是一种循环遍历所有相邻节点,下载其链并使用上述方法验证它们的方法。如果找到一个有效链,其长度大于我们的,我们将替换我们的。

让我们将两个端点注册到我们的API,一个用于添加相邻节点,另一个用于解决冲突:

@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:5000http://localhost:5001

通过python构建一个区块链来学习区块链

然后我在节点2上挖掘了一些新块,以确保链更长。之后,我在节点1上调用了 GET/nodes/resolve ,其中链被共识算法取代:

通过python构建一个区块链来学习区块链

这是一个warp……去找一些朋友一起帮助测试你的区块链。

我希望这能激发你创造新事物。我对Cryptocurrencies感到欣喜若狂,因为我相信Blockchains会迅速改变我们对经济,政府和记录保存的看法。

======================================================================

分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程:

  • EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
  • java以太坊开发教程,主要是针对 java 和android程序员进行区块链以太坊开发的web3j详解。
  • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
  • php以太坊,主要是介绍使用 php 进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。
  • 以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。
  • 以太坊开发进阶教程,主要是介绍使用node.js、 mongodb 、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
  • C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。
  • java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
  • php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
  • tendermint区块链开发详解 ,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是 go 语言工程师快速入门区块链开发的最佳选择。

汇智网原创翻译,转载请标明出处。这里是原文 通过python构建一个区块链来学习区块链


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Haskell School of Music

The Haskell School of Music

Paul Hudak、Donya Quick / Cambridge University Press / 2018-10-4 / GBP 42.99

This book teaches functional programming through creative applications in music and sound synthesis. Readers will learn the Haskell programming language and explore numerous ways to create music and d......一起来看看 《The Haskell School of Music》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具