Python 区块链 - 快速指南


Python 区块链 - 简介

在区块链教程中,我们详细了解了区块链背后的理论。区块链是世界上最受欢迎的数字货币比特币背后的基本组成部分。本教程深入探讨了比特币的复杂性,全面解释了区块链架构。下一步是构建我们自己的区块链。

中本聪创造了世界上第一种虚拟货币,称为比特币。看到比特币的成功,许多其他人创建了自己的虚拟货币。仅举几例:莱特币、Zcash 等。

现在,您可能还想发行自己的货币。我们将其称为 TPCoin(TutorialsPoint Coin)。您将编写一个区块链来记录所有与 TPCoin 相关的交易。TPCoin 可用于购买比萨饼、汉堡、沙拉等。可能会有其他服务提供商加入您的网络并开始接受 TPCoin 作为提供其服务的货币。可能性是无止境。

在本教程中,让我们了解如何构建这样的系统并在市场上推出自己的数字货币。

区块链项目开发涉及的组件

整个区块链项目开发由三个主要部分组成 -

  • 客户
  • 矿工
  • 区块链

客户

客户是将从其他供应商那里购买商品的人。客户本人可能成为供应商,并会根据他提供的货物接受他人的金钱。我们在这里假设客户既可以是 TPCoins 的供应商,也可以是 TPCoins 的接收者。因此,我们将在代码中创建一个能够发送和接收资金的客户端类。

矿工

矿工是从交易池中提取交易并将它们组装成一个块的人。矿工必须提供有效的工作量证明才能获得挖矿奖励。矿工收取的所有费用将由他自己保留。他可能会花这笔钱从网络上的其他注册供应商那里购买商品或服务,就像上述客户所做的那样。

区块链

最后,区块链是一种按时间顺序链接所有开采区块的数据结构。该链是不可变的,因此是防篡改的。

您可以通过在新的 Jupyter 笔记本中键入每个步骤中提供的代码来遵循本教程。或者,您可以从www.anaconda.com下载整个 Jupyter Notebook 。

在下一章中,我们将开发一个使用我们的区块链系统的客户端。

Python 区块链 - 开发客户端

客户是持有 TPCoins 并通过网络上其他供应商(包括他自己的供应商)进行商品/服务交易的人。我们应该为此目的定义一个Client类。为了为客户创建全球唯一的标识,我们使用 PKI(公钥基础设施)。在这一章中,我们就来详细谈谈这个问题。

客户应该能够将钱从他的钱包发送给另一个已知的人。同样,客户应该能够接受来自第三方的资金。为了花钱,客户将创建一个交易,指定发送者的姓名和要支付的金额。为了接收资金,客户将向第三方(本质上是资金的发送者)提供他的身份。我们不会将客户持有的余额存储在他的钱包中。在交易过程中,我们会计算实际余额,以确保客户有足够的余额进行付款。

为了开发Client类以及项目中的其余代码,我们需要导入许多 Python 库。下面列出了这些 -

# import libraries
import hashlib
import random
import string
import json
import binascii
import numpy as np
import pandas as pd
import pylab as pl
import logging
import datetime
import collections

除了上述标准库之外,我们还将签署交易、创建对象的哈希值等。为此,您将需要导入以下库 -

# following imports are required by PKI
import Crypto
import Crypto.Random
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5

下一章我们来谈谈客户类。

Python 区块链 - 客户端类

Client使用内置的 Python RSA算法生成私钥公钥。有兴趣的读者可以参考本教程了解RSA的实现。在对象初始化期间,我们创建私钥和公钥并将它们的值存储在实例变量中。

self._private_key = RSA.generate(1024, random)
self._public_key = self._private_key.publickey()

请注意,您永远不应该丢失您的私钥。为了保存记录,生成的私钥可以复制到安全的外部存储上,或者您可以简单地将其 ASCII 表示形式写在一张纸上。

生成的公钥将用作客户端的身份。为此,我们定义了一个名为“ identity”的属性,它返回公钥的十六进制表示形式。

@property
   def identity(self):
      return
binascii.hexlify(self._public_key.exportKey(format='DER'))
.decode('ascii')

身份对于每个客户来说都是唯一的,并且可以公开提供。任何人都可以使用此身份向您发送虚拟货币,并将其添加到您的钱包中。

Client类的完整代码如下所示 -

class Client:
   def __init__(self):
      random = Crypto.Random.new().read
      self._private_key = RSA.generate(1024, random)
      self._public_key = self._private_key.publickey()
      self._signer = PKCS1_v1_5.new(self._private_key)

   @property
   def identity(self):
      return
binascii.hexlify(self._public_key.exportKey(format='DER')).decode('ascii')

测试客户端

现在,我们将编写代码来说明如何使用Client类 -

Dinesh = Client()
print (Dinesh.identity)

上面的代码创建了一个Client实例并将其分配给变量Dinesh。我们通过调用Dinesh的Identity方法来打印 Dinesh 的公钥。输出如下所示 -

30819f300d06092a864886f70d010101050003818d0030818902818100b547fafceeb131e07
0166a6b23fec473cce22c3f55c35ce535b31d4c74754fecd820aa94c1166643a49ea5f49f72
3181ff943eb3fdc5b2cb2db12d21c06c880ccf493e14dd3e93f3a9e175325790004954c34d3
c7bc2ccc9f0eb5332014937f9e49bca9b7856d351a553d9812367dc8f2ac734992a4e6a6ff6
6f347bd411d07f0203010001

现在,我们将在下一章继续创建交易。

Python 区块链 - 交易类

在本章中,让我们创建一个Transaction类,以便客户端能够向某人汇款。请注意,客户既可以是资金的发送方,也可以是资金的接收方。当您想要收款时,其他发件人将创建一笔交易并在其中指定您的公共地址。我们定义交易类的初始​​化如下 -

def __init__(self, sender, recipient, value):
   self.sender = sender
   self.recipient = recipient
   self.value = value
   self.time = datetime.datetime.now()

init方法采用三个参数 - 发送者的公钥、接收者的公钥和发送的金额。它们存储在实例变量中以供其他方法使用。此外,我们还创建了一个变量来存储交易时间。

接下来,我们编写一个名为to_dict的实用方法,它将上述所有四个实例变量组合在一个字典对象中。这只是为了通过单个变量来访问整个交易信息。

正如您从之前的教程中知道的那样,区块链中的第一个块是创世块。创世区块包含区块链创建者发起的第一笔交易。这个人的身份可能会像比特币一样保密。因此,当创建第一个交易时,创建者可能只是将他的身份发送为Genesis。因此,在创建字典时,我们检查发送者是否是Genesis,如果是,我们只需将一些字符串值分配给标识变量即可;否则,我们将发送者的身份分配给身份变量。

if self.sender == "Genesis":
   identity = "Genesis"
else:
   identity = self.sender.identity

我们使用以下代码行构建字典

return collections.OrderedDict({
   'sender': identity,
   'recipient': self.recipient,
   'value': self.value,
   'time' : self.time})

to_dict方法的完整代码如下所示 -

def to_dict(self):
   if self.sender == "Genesis":
      identity = "Genesis"
   else:
      identity = self.sender.identity

   return collections.OrderedDict({
      'sender': identity,
      'recipient': self.recipient,
      'value': self.value,
      'time' : self.time})

最后,我们将使用发送者的私钥对该字典对象进行签名。和以前一样,我们使用带有 SHA 算法的内置 PKI。生成的签名被解码以获得 ASCII 表示形式,以便打印并将其存储在我们的区块链中。Sign_transaction方法代码如下所示 -

def sign_transaction(self):
   private_key = self.sender._private_key
   signer = PKCS1_v1_5.new(private_key)
   h = SHA.new(str(self.to_dict()).encode('utf8'))
   return binascii.hexlify(signer.sign(h)).decode('ascii')

我们现在将测试这个Transaction类。

测试事务类

为此,我们将创建两个用户,名为DineshRamesh。Dinesh 将发送 5 个 TPCoins 给 Ramesh。为此,我们首先创建名为 Dinesh 和 Ramesh 的客户端。

Dinesh = Client()
Ramesh = Client()

请记住,当您实例化Client类时,将创建客户端特有的公钥和私钥。当 Dinesh 向 Ramesh 发送付款时,他将需要通过使用客户端的身份属性获得的 Ramesh 的公钥。

因此,我们将使用以下代码创建事务实例 -

t = Transaction(
   Dinesh,
   Ramesh.identity,
   5.0
)

请注意,第一个参数是发送方,第二个参数是接收方的公钥,第三个参数是要转账的金额。Sign_transaction方法从第一个参数中检索发送者的私钥以用于签署交易。

创建交易对象后,您将通过调用其sign_transaction方法对其进行签名。此方法以可打印格式返回生成的签名。我们使用以下两行代码生成并打印签名 -

signature = t.sign_transaction()
print (signature) 

当您运行上面的代码时,您将看到类似于此的输出 -

7c7e3c97629b218e9ec6e86b01f9abd8e361fd69e7d373c38420790b655b9abe3b575e343c7
13703ca1aee781acd7157a0624db3d57d7c2f1172730ee3f45af943338157f899965856f6b0
0e34db240b62673ad5a08c8e490f880b568efbc36035cae2e748f1d802d5e8e66298be826f5
c6363dc511222fb2416036ac04eb972

现在,随着创建客户端和交易的基本基础设施准备就绪,我们现在将有多个客户端执行多个交易,就像在现实生活中一样。

创建多个交易

各个客户端进行的交易在系统中排队;矿工从该队列中获取交易并将其添加到区块中。然后他们将开采该区块,获胜的矿工将有权将该区块添加到区块链中,从而为自己赚取一些钱。

我们将在稍后讨论区块链的创建时描述这个挖掘过程。在为多个交易编写代码之前,让我们添加一个小实用函数来打印给定交易的内容。

显示交易

display_transaction函数接受交易类型的单个参数。接收到的事务中的字典对象被复制到一个名为dict的临时变量,并使用字典键,将各种值打印在控制台上。

def display_transaction(transaction):
   #for transaction in transactions:
   dict = transaction.to_dict()
   print ("sender: " + dict['sender'])
   print ('-----')
   print ("recipient: " + dict['recipient'])
   print ('-----')
   print ("value: " + str(dict['value']))
   print ('-----')
   print ("time: " + str(dict['time']))
   print ('-----')

接下来,我们定义一个事务队列来存储事务对象。

交易队列

为了创建队列,我们​​声明一个名为transactions的全局列表变量,如下所示 -

transactions = []

我们将简单地将每个新创建的事务附加到该队列中。请注意,为了简洁起见,我们不会在本教程中实现队列管理逻辑。

创建多个客户端

现在,我们将开始创建交易。首先,我们将创建四个客户,他们将互相汇款以从他人那里获取各种服务或商品。

Dinesh = Client()
Ramesh = Client()
Seema = Client()
Vijay = Client()

此时,我们有四个客户,分别是 Dinesh、Ramesh、Seema 和 Vijay。我们目前假设每个客户的钱包中都持有一些 TPCoins 用于交易。每个客户端的身份将通过使用这些对象的身份属性来指定。

创建第一笔交易

现在,我们启动第一笔交易如下 -

t1 = Transaction(
   Dinesh,
   Ramesh.identity,
   15.0
)

在此交易中,Dinesh 向 Ramesh 发送了 5 个 TPCoins。为了交易成功,我们必须确保 Dinesh 的钱包中有足够的钱来支付这笔款项。请注意,我们需要一个创世交易来启动 TPCoin 在系统中的流通。当您继续阅读时,您很快就会编写此创世交易的交易代码。

我们将使用 Dinesh 的私钥签署此交易并将其添加到交易队列中,如下所示 -

t1.sign_transaction()
transactions.append(t1)

在 Dinesh 进行第一笔交易后,我们将在上面创建的不同客户端之间创建更多交易。

添加更多交易

我们现在将创建更多交易,每笔交易都会向另一方提供一些 TPCoins。当有人花钱时,他没有必要检查这个钱包中是否有足够的余额。无论如何,矿工在发起交易时都会验证发送者所拥有的每笔交易的余额。

如果余额不足,矿工会将这笔交易标记为无效,并且不会将其添加到该区块中。

以下代码创建了另外九个事务并将其添加到我们的队列中。

t2 = Transaction(
   Dinesh,
   Seema.identity,
   6.0
)
t2.sign_transaction()
transactions.append(t2)
t3 = Transaction(
   Ramesh,
   Vijay.identity,
   2.0
)
t3.sign_transaction()
transactions.append(t3)
t4 = Transaction(
   Seema,
   Ramesh.identity,
   4.0
)
t4.sign_transaction()
transactions.append(t4)
t5 = Transaction(
   Vijay,
   Seema.identity,
   7.0
)
t5.sign_transaction()
transactions.append(t5)
t6 = Transaction(
   Ramesh,
   Seema.identity,
   3.0
)
t6.sign_transaction()
transactions.append(t6)
t7 = Transaction(
   Seema,
   Dinesh.identity,
   8.0
)
t7.sign_transaction()
transactions.append(t7)
t8 = Transaction(
   Seema,
   Ramesh.identity,
   1.0
)
t8.sign_transaction()
transactions.append(t8)
t9 = Transaction(
   Vijay,
   Dinesh.identity,
   5.0
)
t9.sign_transaction()
transactions.append(t9)
t10 = Transaction(
   Vijay,
   Ramesh.identity,
   3.0
)
t10.sign_transaction()
transactions.append(t10)

当您运行上述代码时,队列中将有十笔交易供矿工创建区块。

倾销交易

作为区块链管理者,您可能会定期查看交易队列的内容。为此,您可以使用我们之前开发的display_transaction函数。要转储队列中的所有事务,只需迭代事务列表,并为每个引用的事务调用display_transaction函数,如下所示 -

for transaction in transactions:
   display_transaction (transaction)
   print ('--------------')

为了区分,交易之间用虚线分隔。如果运行上面的代码,您将看到如下所示的交易列表 -

sender:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c49214
4a9f463480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329e
c86794b04d773eb4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b
47e5157f8fe56c2ce3279c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311
c4d866c12d79d3fc3034563dfb0203010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e
674abe7abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8ad
d126b6e1a1308fb98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa93977
04de625d1836d3f42c7ee5683f6703259592cc24b09699376807f28fe0e00ff882974484
d805f874260dfc2d1627473b910203010001
-----
value: 15.0
-----
time: 2019-01-14 16:18:01.859915
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c49214
4a9f463480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329e
c86794b04d773eb4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b
47e5157f8fe56c2ce3279c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311
c4d866c12d79d3fc3034563dfb0203010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae14
3cbe59b3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fb
d9ee74b9e7ea12334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0
961b4f212d1fd5b5e49ae09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d0623
75799742a359b8f22c5362e5650203010001
-----
value: 6.0
-----
time: 2019-01-14 16:18:01.860966
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e
674abe7abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8ad
d126b6e1a1308fb98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa93977
04de625d1836d3f42c7ee5683f6703259592cc24b09699376807f28fe0e00ff882974484
d805f874260dfc2d1627473b910203010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100cba097c0854876
f41338c62598c658f545182cfa4acebce147aedf328181f9c4930f14498fd03c0af6b0cc
e25be99452a81df4fa30a53eddbb7bb7b203adf8764a0ccd9db6913a576d68d642d8fd47
452590137869c25d9ff83d68ebe6d616056a8425b85b52e69715b8b85ae807b84638d8f0
0e321b65e4c33acaf6469e18e30203010001
-----
value: 2.0
-----
time: 2019-01-14 16:18:01.861958
-----
--------------

为了简洁起见,我只打印了列表中的前几笔交易。在上面的代码中,我们打印从第一个交易开始的所有交易,除了从未添加到此列表中的创世交易。由于交易会定期添加到区块中,因此您通常只想查看尚未挖掘的交易列表。在这种情况下,您将需要创建一个适当的for循环来迭代尚未挖掘的交易。

到目前为止,您已经学习了如何创建客户端、允许它们相互连接以及维护要挖掘的待处理事务的队列。现在,本教程最重要的部分是创建区块链本身。您将在下一课中学习这一点。

Python 区块链 - 块类

一个区块由不同数量的交易组成。为了简单起见,在我们的例子中,我们假设该块由固定数量的交易组成,在本例中为三个。由于块需要存储这三个交易的列表,我们将声明一个名为Verified_transactions的实例变量,如下所示 -

self.verified_transactions = []

我们将此变量命名为verified_transactions,以表明只有经过验证的有效交易才会被添加到区块中。每个块还保存前一个块的哈希值,因此块链变得不可变。

为了存储先前的哈希值,我们声明一个实例变量,如下所示 -

self.previous_block_hash = ""

最后,我们声明一个名为Nonce的变量,用于存储矿工在挖矿过程中创建的随机数。

self.Nonce = ""

Block类的完整定义如下 -

class Block:
   def __init__(self):
      self.verified_transactions = []
      self.previous_block_hash = ""
      self.Nonce = ""

由于每个块都需要前一个块的哈希值,因此我们声明一个名为last_block_hash的全局变量,如下所示 -

last_block_hash = ""

现在让我们在区块链中创建第一个区块。

Python 区块链 - 创建创世块

我们假设 TPCoins 的发起者最初向已知客户Dinesh赠送 500 TPCoins 。为此,他首先创建一个 Dinesh 实例 -

Dinesh = Client()

然后我们创建一个创世交易并将 500 TPCoins 发送到 Dinesh 的公共地址。

t0 = Transaction (
   "Genesis",
   Dinesh.identity,
   500.0
)

现在,我们创建Block类的一个实例并将其命名为block0

block0 = Block()

我们将previous_block_hashNonce实例变量初始化为None,因为这是存储在区块链中的第一个交易。

block0.previous_block_hash = None
Nonce = None

接下来,我们将上面的 t0 交易添加到块内维护的verify_transactions列表中 -

block0.verified_transactions.append (t0)

此时,该块已完全初始化并准备好添加到我们的区块链中。我们将为此目的创建区块链。在将块添加到区块链之前,我们将对块进行哈希处理并将其值存储在我们之前声明的名为last_block_hash的全局变量中。该值将被其区块中的下一个矿工使用。

我们使用以下两行代码来对块进行哈希处理并存储摘要值。

digest = hash (block0)
last_block_hash = digest

最后,我们创建一个区块链,正如我们在下一章中看到的那样。

Python 创建区块链

区块链包含一系列相互链接的区块。为了存储整个列表,我们将创建一个名为 TPCoins 的列表变量 -

TPCoins = []

我们还将编写一个名为dump_blockchain 的实用方法来转储整个区块链的内容。我们首先打印区块链的长度,以便我们知道区块链中当前存在多少个区块。

def dump_blockchain (self):
   print ("Number of blocks in the chain: " + str(len (self)))

请注意,随着时间的推移,区块链中的区块数量将非常多,无法打印。因此,当您打印区块链的内容时,您可能必须决定要检查的范围。在下面的代码中,我们打印了整个区块链,因为我们不会在当前演示中添加太多块。

为了迭代链,我们设置一个for循环,如下所示 -

for x in range (len(TPCoins)):
   block_temp = TPCoins[x] 

每个引用的块都被复制到一个名为block_temp的临时变量。

我们打印块号作为每个块的标题。请注意,数字将从零开始,第一个块是编号为零的创世块。

print ("block # " + str(x))

在每个块中,我们将三个交易的列表(创世块除外)存储在名为verify_transactions的变量中。我们在for循环中迭代此列表,对于每个检索到的项目,我们调用display_transaction函数来显示交易详细信息。

for transaction in block_temp.verified_transactions:
   display_transaction (transaction)

整个函数定义如下所示 -

def dump_blockchain (self):
   print ("Number of blocks in the chain: " + str(len (self)))
   for x in range (len(TPCoins)):
      block_temp = TPCoins[x]
      print ("block # " + str(x))
      for transaction in block_temp.verified_transactions:
         display_transaction (transaction)
         print ('--------------')
      print ('=====================================')

请注意,这里我们在代码中的适当位置插入了分隔符,以划分其中的块和交易。

由于我们现在已经创建了一个用于存储块的区块链,因此我们的下一个任务是创建块并开始将其添加到区块链中。为此,我们将添加您在前面的步骤中创建的创世块。

Python 区块链 - 添加创世块

向区块链添加区块涉及将创建的区块追加到我们的TPCoins列表中。

TPCoins.append (block0)

请注意,与系统中的其他块不同,创世块仅包含一笔由 TPCoins 系统发起者发起的交易。现在,您将通过调用我们的全局函数dump_blockchain来转储区块链的内容-

dump_blockchain(TPCoins)

当您执行此函数时,您将看到以下输出 -

Number of blocks in the chain: 1
block # 0
sender: Genesis
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100ed272b52ccb539
e2cd779c6cc10ed1dfadf5d97c6ab6de90ed0372b2655626fb79f62d0e01081c163b0864
cc68d426bbe9438e8566303bb77414d4bfcaa3468ab7febac099294de10273a816f7047d
4087b4bafa11f141544d48e2f10b842cab91faf33153900c7bf6c08c9e47a7df8aa7e60d
c9e0798fb2ba3484bbdad2e4430203010001
-----
value: 500.0
-----
time: 2019-01-14 16:18:02.042739
-----
--------------
=====================================

至此,区块链系统就可以使用了。我们现在将通过为感兴趣的客户提供挖矿功能来使他们成为矿工。

Python 区块链 - 创建矿工

为了实现挖矿,我们需要开发一个挖矿功能。挖掘功能需要生成给定消息字符串的摘要并提供工作证明。让我们在本章中讨论这个问题。

消息摘要功能

我们将编写一个名为sha256的实用函数,用于在给定消息上创建摘要 -

def sha256(message):
return hashlib.sha256(message.encode('ascii')).hexdigest()

sha256函数将消息作为参数,将其编码为 ASCII,生成十六进制摘要并将值返回给调用者

挖矿功能

我们现在开发了实现我们自己的挖矿策略的挖矿功能。在这种情况下,我们的策略是对给定消息生成一个哈希值,该消息以给定数量的 1 为前缀。给定的 1 数量被指定为函数的参数,该函数被指定为难度级别。

例如,如果您指定难度级别为 2,则给定消息生成的哈希值应以两个 1 开头 - 例如 11xxxxxxxx。如果难度级别为 3,则生成的哈希值应以三个 1 开头 - 例如 111xxxxxxxx。考虑到这些要求,我们现在将开发挖掘功能,如下所示的步骤。

步骤1

挖掘函数有两个参数——消息和难度级别。

def mine(message, difficulty=1):

第2步

难度级别需要大于或等于 1,我们通过以下断言语句来确保这一点 -

assert difficulty >= 1

步骤3

我们使用设置的难度级别创建一个前缀变量。

prefix = '1' * difficulty

请注意,如果难度级别为 2,则前缀将为“11”,如果难度级别为 3,则前缀将为“111”,依此类推。我们将检查生成的消息摘要中是否存在该前缀。为了消化消息本身,我们使用以下两行代码 -

for i in range(1000):
   digest = sha256(str(hash(message)) + str(i))

我们在每次迭代中不断向消息哈希添加新的数字i ,并在组合消息上生成新的摘要。随着sha256函数的输入在每次迭代中发生变化,摘要值也会发生变化。我们检查这个摘要值是否具有上面设置的前缀

if digest.startswith(prefix):

如果条件满足,我们将终止for循环并将摘要值返回给调用者。

整个矿井代码如下所示 -

def mine(message, difficulty=1):
   assert difficulty >= 1
   prefix = '1' * difficulty
   for i in range(1000):
      digest = sha256(str(hash(message)) + str(i))
      if digest.startswith(prefix):
         print ("after " + str(i) + " iterations found nonce: "+ digest)
      return digest

为了您的理解,我们添加了打印语句,用于打印摘要值以及从函数返回之前满足条件所需的迭代次数。

测试挖矿功能

要测试我们的挖掘功能,只需执行以下语句 -

mine ("test message", 2)

当您运行上述代码时,您将看到类似于以下内容的输出 -

after 138 iterations found nonce:
11008a740eb2fa6bf8d55baecda42a41993ca65ce66b2d3889477e6bfad1484c

请注意,生成的摘要以“11”开头。如果将难度级别更改为 3,生成的摘要将从“111”开始,当然,它可能需要更多的迭代次数。正如您所看到的,具有更强处理能力的矿工将能够更早地挖掘给定的消息。这就是矿工们相互竞争赚取收入的方式。

现在,我们准备向区块链添加更多区块。让我们在下一章中学习这一点。

Python 区块链 - 添加区块

每个矿工将从之前创建的交易池中获取交易。为了跟踪已挖掘的消息数量,我们必须创建一个全局变量 -

last_transaction_index = 0

现在,我们的第一个矿工将向区块链添加一个区块。

添加第一个块

要添加新块,我们首先创建Block类的实例。

block = Block()

我们从队列中选取前 3 个交易 -

for i in range(3):
   temp_transaction = transactions[last_transaction_index]
   # validate transaction

在将交易添加到区块之前,矿工将验证交易的有效性。通过测试发送者提供的哈希与矿工使用发送者的公钥生成的哈希是否相等来验证交易有效性。此外,矿工将验证发送者是否有足够的余额来支付当前交易。

为简洁起见,我们没有在本教程中包含此功能。交易验证后,我们将其添加到实例中的verified_transactions列表中。

block.verified_transactions.append (temp_transaction)

我们增加最后一个交易索引,以便下一个矿工将拾取队列中的后续交易。

last_transaction_index += 1

我们将三笔交易添加到区块中。完成此操作后,我们将初始化Block类的其余实例变量。我们首先添加最后一个块的哈希值。

block.previous_block_hash = last_block_hash

接下来,我们开采难度级别为 2 的区块。

block.Nonce = mine (block, 2)

请注意, mine函数的第一个参数是一个二进制对象。现在,我们对整个块进行哈希处理并在其上创建摘要。

digest = hash (block)

最后,我们将创建的块添加到区块链中,并重新初始化全局变量last_block_hash以供下一个块使用。

添加块的完整代码如下所示 -

block = Block()
for i in range(3):
   temp_transaction = transactions[last_transaction_index]
   # validate transaction
   # if valid
   block.verified_transactions.append (temp_transaction)
   last_transaction_index += 1

block.previous_block_hash = last_block_hash
block.Nonce = mine (block, 2)
digest = hash (block)
TPCoins.append (block)
last_block_hash = digest

添加更多块

我们现在将在区块链中添加两个区块。添加接下来两个块的代码如下 -

# Miner 2 adds a block
block = Block()

for i in range(3):
   temp_transaction = transactions[last_transaction_index]
   # validate transaction
   # if valid
   block.verified_transactions.append (temp_transaction)
   last_transaction_index += 1
block.previous_block_hash = last_block_hash
block.Nonce = mine (block, 2)digest = hash (block)
TPCoins.append (block)last_block_hash = digest
# Miner 3 adds a block
block = Block()

for i in range(3):
   temp_transaction = transactions[last_transaction_index]
   #display_transaction (temp_transaction)
   # validate transaction
   # if valid
   block.verified_transactions.append (temp_transaction)
   last_transaction_index += 1

block.previous_block_hash = last_block_hash
block.Nonce = mine (block, 2)
digest = hash (block)

TPCoins.append (block)
last_block_hash = digest

当您添加这两个块时,您还将看到找到 Nonce 所需的迭代次数。此时,我们的区块链总共由 4 个区块(包括创世区块)组成。

转储整个区块链

您可以使用以下语句验证整个区块链的内容 -

dump_blockchain(TPCoins)

您将看到类似于下面所示的输出 -

Number of blocks in the chain: 4
block # 0
sender: Genesis
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100ed272b52ccb539e2cd779
c6cc10ed1dfadf5d97c6ab6de90ed0372b2655626fb79f62d0e01081c163b0864cc68d426bbe943
8e8566303bb77414d4bfcaa3468ab7febac099294de10273a816f7047d4087b4bafa11f141544d4
8e2f10b842cab91faf33153900c7bf6c08c9e47a7df8aa7e60dc9e0798fb2ba3484bbdad2e44302
03010001
-----
value: 500.0
-----
time: 2019-01-14 16:18:02.042739
-----
--------------
=====================================
block # 1
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c492144a9f463
480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329ec86794b04d773e
b4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b47e5157f8fe56c2ce3279
c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311c4d866c12d79d3fc3034563dfb02
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
value: 15.0
-----
time: 2019-01-14 16:18:01.859915
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c492144a9f463
480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329ec86794b04d773e
b4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b47e5157f8fe56c2ce3279
c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311c4d866c12d79d3fc3034563dfb02
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
value: 6.0
-----
time: 2019-01-14 16:18:01.860966
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100cba097c0854876f41338c
62598c658f545182cfa4acebce147aedf328181f9c4930f14498fd03c0af6b0cce25be99452a81d
f4fa30a53eddbb7bb7b203adf8764a0ccd9db6913a576d68d642d8fd47452590137869c25d9ff83
d68ebe6d616056a8425b85b52e69715b8b85ae807b84638d8f00e321b65e4c33acaf6469e18e302
03010001
-----
value: 2.0
-----
time: 2019-01-14 16:18:01.861958
-----
--------------
=====================================
block # 2
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
value: 4.0
-----
time: 2019-01-14 16:18:01.862946
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100cba097c0854876f41338c
62598c658f545182cfa4acebce147aedf328181f9c4930f14498fd03c0af6b0cce25be99452a81d
f4fa30a53eddbb7bb7b203adf8764a0ccd9db6913a576d68d642d8fd47452590137869c25d9ff83
d68ebe6d616056a8425b85b52e69715b8b85ae807b84638d8f00e321b65e4c33acaf6469e18e302
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
value: 7.0
-----
time: 2019-01-14 16:18:01.863932
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
value: 3.0
-----
time: 2019-01-14 16:18:01.865099
-----
--------------
=====================================
block # 3
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c492144a9f463
480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329ec86794b04d773e
b4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b47e5157f8fe56c2ce3279
c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311c4d866c12d79d3fc3034563dfb02
03010001
-----
value: 8.0
-----
time: 2019-01-14 16:18:01.866219
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
value: 1.0
-----
time: 2019-01-14 16:18:01.867223
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100cba097c0854876f41338c
62598c658f545182cfa4acebce147aedf328181f9c4930f14498fd03c0af6b0cce25be99452a81d
f4fa30a53eddbb7bb7b203adf8764a0ccd9db6913a576d68d642d8fd47452590137869c25d9ff83
d68ebe6d616056a8425b85b52e69715b8b85ae807b84638d8f00e321b65e4c33acaf6469e18e302
03010001
-----
recipient: 
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c492144a9f463
480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329ec86794b04d773e
b4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b47e5157f8fe56c2ce3279
c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311c4d866c12d79d3fc3034563dfb02
03010001
-----
value: 5.0
-----
time: 2019-01-14 16:18:01.868241
-----
--------------
=====================================

Python 区块链 - 范围和结论

在本教程中,我们学习了如何用 Python 构建区块链项目。您需要在许多方面向该项目添加更多功能。

例如,您需要编写用于管理事务队列的函数。当交易被开采并且开采的区块被系统接受后,它们不需要再被存储。

此外,矿工肯定更愿意接受费用最高的交易。同时,您必须确保低费用或免费交易不会永远挨饿。

您将需要开发管理队列的算法。此外,当前教程不包括客户端界面代码。您需要为普通客户和矿工开发此功能。成熟的区块链项目将运行更多的代码行,超出了本教程的范围。有兴趣的读者可以下载比特币源码进一步研究。

结论

这个清晰的教程应该可以帮助您开始创建自己的区块链项目。

对于成熟的区块链项目开发,您可以从比特币源码中了解更多信息。

对于较大的商业或非商业项目,您可以考虑使用以太坊- 一个随时可用的区块链应用程序平台。