参考自:Learn Blockchains by Building One

数字货币的崛起在这些年无比惊艳,区块链这个本来陌生的概念这两年,尤其是今年以来甚至都要盖过了人工智能。但对于不懂密码学、不懂共识协议、也不怎么敢炒币的同学来说,怎么样了解这一新兴的概念以让自己不被时代淘汰,怎么样让心里那一小小的对风口的渴望成为现实,也就是,**怎么样了解区块链的本质,以将这种技术落地转换成真正的商业模式?**相信,这是很多同学都想要知道的问题。

而想要深刻的理解区块链到底是个东西,办法很简单,知行合一,做一个出来。

准备

预备知识

阅读本文,需要读者对Python有基本的理解,能读写基本的Python,并且需要了解HTTP网络协议。

区块链狭义上是分布式账本,广义上是分布式基础架构与计算方式

如果您对区块链还不是太了解,可阅读 什么是区块链

区块链在数据结构上是由区块的记录构成的不可变、有序的链结构,记录可以是交易、文件或任何你想要的数据,它们之间通过**哈希值(hashes)**进行链接。

环境准备

环境准备,确保已经安装Python3.6+, pip , Flask, requests

安装方法:

pip install Flask==0.12.2 requests==2.18.4

同时还需要一个HTTP客户端,比如PostmancURL或其它客户端。

开发

代码地址:https://github.com/zgljl2012/py_blockchain_learn

新建一个项目(建议使用VSCode。。。),然后新建一个 .py 文件 blockchain.py,我们将在这个文件里定义区块链类。

Blockchain类

创建一个Blockchain类,在构造函数中创建了两个列表,一个用于储存区块链,一个用于储存交易。

代码如下:

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

以上是区块链类的各个接口:

  • 构造函数: 中创建了两个列表,一个用于储存区块,一个用于储存交易
  • new_block: 创建一个新区块并将其加入链中
  • new_transaction: 创建交易,并将其加入列表中
  • hash: 对区块进行hash计算
  • last_block: 获取最后一个区块

区块的结构

每个区块包含属性:

  • 索引(index
  • Unix时间戳(timestamp
  • 交易列表(transactions
  • 工作量证明(proof
  • 前一个区块的Hash值。

区块的结构如下:

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

通过以上区块链的结构,相信大家对区块链的原理有了进一步的了解:每个区块都有上一区块的Hash值,从而形成了一条链;如果上一个区块变了,那么后面的区块都将不正确了,保证了区块链的安全;交易保存在区块内。

添加交易

下面我们添加一个交易,实现new_transaction方法

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

每笔交易记录了转账发起人地址、转账接收人地址和每笔数额;然后将该笔交易所处区块的索引返回。因为这笔交易将记录在下一个产生的区块里,所以是上一个的区块索引+1

创建区块

实例化Blockchain后,下面我们开始构造创世块(没有前区块的第一个区块),并且给它加上一个工作量证明。每个区块都将需要经过工作量证明,即挖矿(每个区块都是被“挖”出来的)。

接下来,我们实现 new_block(), new_transaction()hash()

首先引入几个包:

from time import time
import json
import hashlib

接下来,是几个方法的实现:

    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):
        """
        生成新区块
        :param proof: <int> 工作量证明
        :param previous_hash: (Optional) <str> 前一个区块的Hash值
        :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
    
    @property
    def last_block(self):
        return self.chain[-1]

    @staticmethod
    def hash(block):
        """
        生成块的 SHA-256 hash值
        :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

这部分有些复杂,我们分为三步讲解:

  1. 什么是PoW(工作量证明)?
  2. 为什么要进行工作量证明?
  3. 如何进行工作量证明?

什么是PoW

工作量证明(Proof Of Work,简称PoW),简单理解就是一份证明,用来确认你做过一定量的工作。监测工作的整个过程通常是极为低效的,而通过对工作的结果进行认证来证明完成了相应的工作量,则是一种非常高效的方式。

比如毕业证书。我们找工作时,如何证明自己接受过高等教育,是大学毕业呢?很简单,通过毕业证书。工作量证明亦是如此。

为什么要进行PoW

这里我们需要回溯一下比特币的原理。

区块链是由区块构成的,交易存储在区块中,然后每个区块是通过“挖矿”产生的。“挖矿”是什么样的一个过程呢?就是全网所有的“人”都做同一个工作,谁做得最快,那么这个区块就是谁“挖”出的,然后这个“人”就获得系统的奖励——一个比特币。

好了,问题就来了,怎么证明这个“人”的工作做得最好呢?怎么样让全网所有“人”达成**“共识”**都说这个人做得最快呢?

答案:工作量证明。

同时,这种让全网所有人达成共识,一致认定这个“人”做得最快的协议就叫做“共识协议”。共识协议不止Pow这一种,相信大家已经所有耳闻。

如何进行工作量证明

上面我们说到了,工作量证明的目的是让全网所有人达成共识,有一个“人”工作做得最快,然后这个“人”产生一个区块并获得一个比特币,那么这里就又出现了几个问题了:

  1. 全网所有人做什么工作?这个工作必须比较困难,耗时要长一点
  2. 大家怎么验证结果是正确的?

做什么工作

PoW共识协议要求全网所有“人”做的工作是:找出一个符合特定条件的数字。

比如:

假设一个整数 x 乘以另一个整数 y 的积的 Hash 值必须以 0 结尾,即 hash(x * y) = ac23dc…0。设变量 x = 5,求 y 的值?

Python代码如下:

from hashlib import sha256
x = 5
y = 0  # y未知
while sha256(f'{x*y}'.encode()).hexdigest()[-1] != "0":
    y += 1
print(f'The solution is y = {y}')

好了,所有的矿工只要跑上面的代码,不断的循环计算就好了,直到符合条件的y出来为止。

如何验证结果

结果是y=21. 因为:

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

这样就验证了结果。

我们的PoW

好,下面就我们也来设计一个 PoW 算法,首先是定义一下“工作”的规则:

寻找一个数p,使得它与前一个区块的 proof (工作量) 拼接成的字符串的 Hash 值以 2 个零开头

为了避免太浪费时间,我们的规则是比较简单的。

首先修改 blackchain.py 的代码。

首先引入1个包:

from uuid import uuid4

然后添加代码:

    def proof_of_work(self, last_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):
        """
        工作量验证
        :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[:2] == "00"

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

好了,到此为止,我们的Blockchain基本就完成了。希望坚持看到了这里的同学,能再坚持坚持~,接下来的东西对于不懂网络(HTPP)、不懂后台开发的同学会稍有些吃力了。尽量让大家测试、实践的时候能方便些。

代码地址:https://github.com/zgljl2012/py_blockchain_learn