首页 > 技术文章 > Python创建区块链

sms369 2021-04-19 16:14 原文

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

 

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

推荐阅读