学习区块链的最好方法是构建一个(上)

新闻来源    2018年06月06日 09:06

前言:深入理解区块链最好的方式莫过于亲手搭建一个,在这个过程中理解它背后的逻辑和原理。本文作者是Danielvan Flymen ,文章来源于hackernoon.com,由蓝狐笔记社群“iGreenMind”翻译。

你来这里是因为和我一样,你对加密货币的崛起感到兴奋。你想了解区块链是如何工作的,它们背后的基本技术原理是怎样的。

但了解区块链并不容易,至少对我来说不是很容易的。我喜欢边干边学。它迫使我在代码级别上处理问题,这种方法可以让我坚持学习下去。

记住,区块链是一个不可变的、有顺序的链记录,我们称之为区块。它们可以包含交易、文件或任何你想要的数据。但重要的是它们是用哈希链接在一起。




这个指南最适合的阅读对象的要求是什么?至少你轻松地阅读和编写一些基本的Python,并了解HTTP请求是如何工作的,因为我们将通过HTTP协议与我们的 Blockchain 进行交互。

需要什么工具和环境?确保安装了Python 3.6+(以及 pip ),还需要安装Flask和Requests库:

pip install Flask==0.12.2 requests==2.18.4

你还需要一个HTTP客户端,比如Postman或cURL。可用的源代码请点击:https://github.com/dvf/blockchain

第一步:构建Blockchain

打开你喜欢的文本编辑器或IDE,我比较喜欢使用 PyCharm。然后创建一个名为blockchain.py的新文件。只使用这一个文件,但是如果搞丢了此文件,你可以一直引用源代码: https://github.com/dvf/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

区块链 class 负责管理链。它将存储交易,并有一些辅助方法来为链添加新的区块。让我们开始充实一些方法。

一个区块会是什么样子?

每个块都有一个索引、一个时间戳(Unix时间)、一个交易列表、一个证明和前一个块的哈希值。

区块源码例子:

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

链的概念应该很明显:每个新块都包含在其内部的前一个块的哈希。 这点是至关重要的,因为它使 Blockchain 不可篡改
:如果攻击者破坏了链中较早的区块,那么随后所有的块都将包含不正确的哈希值。

请花一些时间好好去理解它——这是区块链设计的的核心理念。

在区块中添加交易

我们需要一种将交易添加到块中的方法。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:  Address of the Sender    
       :param recipient:  Address of the Recipient    
       :param amount:  Amount    
       :return:  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()将交易添加到列表之后,它将返回这个交易会被添加到下一个块的索引。这对稍后提交交易的用户有用。

创建新区块

当 区块链被实例化时,需要将它与一个没有前辈的创世区块一起连接起来。我们还需要向我们的创世区块添加一个“证明”,这是挖矿的结果。

除了在我们的构造函数中创建创世区块之外,我们还将为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:  The proof given by the Proof of Work algorithm
        :param previous_hash: (Optional)  Hash of previous Block
        :return:  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:  Address of the Sender
        :param recipient:  Address of the Recipient
        :param amount:  Amount
        :return:  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:  Block
        :return:         """

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

至此,我们几乎完成了 Blockchain 的代码化表现。但新的区块是如何被创建、挖掘的?

理解PoW工作量证明

工作量证明,也就是新的区块如何在 Blockchain 上被创建或挖掘出来。它的目标是发现一个解决问题的数字,这个数字一定 很难找到,但却很容易被验证
——在网络上的任何人都可以通过计算来验证,这是工作证明PoW背后的核心思想。

我们来看一个非常简单的例子:我们想找到这样一个数值,将整数x与另一个数值y的乘积进行hash运算,使得运算的结果是一串字符串的结尾必须是数字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,当与上一个区块的解进行哈希运算时,产生一个前4位都是0的哈希值。

为了调整算法的难度,我们可以修改前几位零的个数。但4个0就足够了。你将发现,添加一个前导零就会对找到解所需的时间造成很大的不同。

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: 

 
        :return: 
 
  
        """

        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: 
  
    Previous Proof
        :param proof: 
   
     Current Proof
        :return: 
    
      True if correct, False if not.
        """

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

我们的类接近完成,我们已经准备好使用HTTP请求开始与它交互。

第二步:将区块链作为API使用起来

使用Python的Flask框架。它是一个微型框架,它可以很容易地将端点映射到Python函数。这让我们使用HTTP请求在web上与 Blockchain 进行交互。

我们将创建三个方法:

  • /transactions/new 创建一个新的交易到一个区块。

  • /mine 告诉我们的服务器去挖掘一个新的区块。

  • /chain 返回完整的 Blockchain 。

设置Flask

我们的“服务器”将在 Blockchain 网络中形成单独节点,创建一些样板代码如下所示:

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)

关于在上面代码中添加的内容的简要说明如下:

  • Line 15: 实例化节点。

  • Line 18: 为我们的节点创建一个随机名称。

  • Line 21: 实例化我们的Blockchain类。

  • Line 24–26: 创建/mine 端点,这是一个GET请求。

  • Line 28–30: 创建 /transactions/new 端点,这是一个POST 请求,因为我们将向它发送数据。

  • Line 32–38: 创建/chain端点,它返回完整的 Blockchain 。

  • Line 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. 计算工作量证明。

  2. 通过增加一笔交易,奖赏给矿工(也就是我们自己)一定量的数字货币。

  3. 通过将新区块添加到链中来锻造区块。

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 class中的方法进行交互。在这一点上,我们已经完成了,并且可以开始与我们的 Blockchain 进行交互了。

——未完待续——


新闻来源


CryptoCurrencyCNYChange 1hChange 24hChange 7d
Bitcoin369,917 0.43 % 1.12 % 2.08 %
Ethereum28,411 0.00 % 3.68 % 2.97 %
Binance Coin3,970.5 0.03 % 1.60 % 5.30 %
Tether6.490 0.02 % 0.10 % 0.00 %
Solana120.65 1.27 % 1.81 % 28.37 %
Cardano14.22 0.42 % 4.87 % 0.22 %
XRP6.950 0.74 % 0.44 % 13.05 %
Polkadot186.24 0.08 % 15.21 % 37.87 %
USD Coin6.450 0.19 % 0.45 % 0.44 %
Dogecoin2.050 0.07 % 5.76 % 12.58 %
Avalanche263.57 1.24 % 9.58 % 126.95 %
Shiba Inu0.00005447 2.36 % 8.57 % 35.74 %
Terra135.52 0.04 % 1.40 % 47.29 %
Crypto.com Coin1.460 0.20 % 4.39 % 9.05 %
Wrapped Bitcoin219,027 0.89 % 1.38 % 3.42 %
Litecoin2,003.2 1.07 % 7.23 % 35.68 %
Binance USD6.490 0.38 % 0.60 % 0.69 %
Polygon6.530 0.79 % 0.08 % 13.45 %
Chainlink159.12 0.87 % 1.57 % 4.71 %
Algorand11.89 1.63 % 5.73 % 6.03 %
Bitcoin Cash6,714.1 0.17 % 13.74 % 49.71 %
Uniswap259.87 1.14 % 12.19 % 0.43 %
Axie Infinity777.97 1.22 % 10.06 % 0.62 %
Dai6.440 0.16 % 0.37 % 0.52 %
VeChain0.3343 0.42 % 4.52 % 46.37 %
Stellar3.260 0.41 % 6.68 % 0.28 %
Elrond2,530.7 1.18 % 3.62 % 10.14 %
Cosmos136.82 0.59 % 0.43 % 7.91 %
Internet Computer264.48 5.71 % 9.44 % 36.93 %
TerraUSD6.400 0.00 % 0.09 % 0.22 %