Python 区块链 - 快速指南



Python 区块链 - 简介

在本区块链教程中,我们详细学习了区块链背后的理论。区块链是世界上最流行的数字货币比特币背后的基本构建块。本教程深入探讨了比特币的复杂性,充分解释了区块链架构。下一步是构建我们自己的区块链。

中本聪创建了世界上第一种虚拟货币比特币。鉴于比特币的成功,许多人创建了自己的虚拟货币。举几个例子——莱特币、Zcash等等。

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

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

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

整个区块链项目开发包括三个主要组件:

  • 客户端
  • 矿工
  • 区块链

客户端

客户端是向其他供应商购买商品的人。客户端本身可以成为供应商,并接受他人支付的商品款项。我们在此假设客户端既可以是 TPCoin 的供应商,也可以是接收者。因此,我们将在代码中创建一个客户端类,该类具有发送和接收货币的能力。

矿工

矿工是从交易池中提取交易并将它们组装成区块的人。矿工必须提供有效的“工作量证明”才能获得挖矿奖励。矿工收取的所有费用都归其所有。他可以用这笔钱从网络上的其他注册供应商那里购买商品或服务,就像上面描述的客户端一样。

区块链

最后,区块链是一种数据结构,它按时间顺序将所有已挖出的区块链接在一起。这个链是不可变的,因此是防篡改的。

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

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

Python 区块链 - 开发客户端

客户端是持有 TPCoin 并将其用于从网络上的其他供应商(包括他自己)处交易商品/服务的人。为此,我们应该定义一个客户端类。为了创建客户端的全局唯一标识,我们使用 PKI(公钥基础设施)。在本章中,让我们详细讨论一下。

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

为了开发客户端类以及项目中的其余代码,我们需要导入许多 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 区块链 - 客户端类

客户端类使用内置的 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')

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

此处显示客户端类的完整代码:

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

测试客户端

现在,我们将编写代码来说明如何使用客户端类:

Dinesh = Client()
print (Dinesh.identity)

上面的代码创建了一个客户端实例并将其赋值给变量Dinesh。我们通过调用其identity方法来打印Dinesh的公钥。输出如下所示:

30819f300d06092a864886f70d010101050003818d0030818902818100b547fafceeb131e07
0166a6b23fec473cce22c3f55c35ce535b31d4c74754fecd820aa94c1166643a49ea5f49f72
3181ff943eb3fdc5b2cb2db12d21c06c880ccf493e14dd3e93f3a9e175325790004954c34d3
c7bc2ccc9f0eb5332014937f9e49bca9b7856d351a553d9812367dc8f2ac734992a4e6a6ff6
6f347bd411d07f0203010001

现在,让我们在下一章中继续创建一个交易。

Python 区块链 - 交易类

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

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,如果是,我们只需将某个字符串值赋给identity变量;否则,我们将发送者的身份赋给identity变量。

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

最后,我们将使用发送者的私钥对这个字典对象进行签名。和以前一样,我们使用内置的 PKI 和 SHA 算法。生成的签名被解码以获取 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')

我们现在将测试这个交易类。

测试交易类

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

Dinesh = Client()
Ramesh = Client()

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

因此,我们将使用以下代码创建交易实例:

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。我们目前假设这些客户端中的每一个在其钱包中都持有一些 TPCoin 用于交易。每个客户端的身份将使用这些对象的 identity 属性指定。

创建第一笔交易

现在,我们启动我们的第一笔交易,如下所示:

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

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

我们将使用 Dinesh 的私钥对这笔交易进行签名,并将其添加到交易队列中,如下所示:

t1.sign_transaction()
transactions.append(t1)

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

添加更多交易

接下来,我们将创建更多交易,每笔交易向另一方发放少量TPCoin。当有人花钱时,不必检查此钱包中的余额是否足够。矿工无论如何都会在发起交易时验证发送方拥有的余额。

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

以下代码创建并向我们的队列中添加了九个交易。

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的变量,用于存储矿工在挖掘过程中创建的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区块链 - 创建创世区块

我们假设TPCoin的发起者最初向已知的客户端Dinesh发放500个TPCoin。为此,他首先创建一个Dinesh实例:

Dinesh = Client()

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

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

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

block0 = Block()

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

block0.previous_block_hash = None
Nonce = None

接下来,我们将上述t0交易添加到区块中维护的verified_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))

在每个区块中,我们都将三个交易(创世区块除外)的列表存储在一个名为verified_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)

请注意,与系统中的其他区块不同,创世区块只包含一个交易,该交易由TPCoin系统的发起者发起。现在,您将通过调用我们的全局函数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函数将message作为参数,将其编码为ASCII,生成十六进制摘要并将值返回给调用者。

挖掘函数

现在我们开发mine函数,该函数实现我们自己的挖掘策略。在这种情况下,我们的策略是在给定的消息上生成一个以给定数量的1开头哈希值。给定数量的1作为参数指定给mine函数,指定为难度级别。

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

步骤1

挖掘函数接受两个参数 - 消息和难度级别。

def mine(message, difficulty=1):

步骤2

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

assert difficulty >= 1

步骤3

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

prefix = '1' * difficulty

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

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

我们在每次迭代中都会向消息哈希中添加一个新的数字i,并在组合的消息上生成一个新的摘要。由于sha256函数的输入在每次迭代中都会发生变化,因此digest值也会发生变化。我们检查此digest值是否具有上述prefix

if digest.startswith(prefix):

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

完整的mine代码如下所示:

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

为了您的理解,我们添加了print语句,该语句打印摘要值以及满足条件之前所花费的迭代次数,然后再从函数返回。

测试挖掘函数

要测试我们的挖掘函数,只需执行以下语句:

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

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

为简洁起见,我们没有在本教程中包含此功能。验证交易后,我们将其添加到block实例中的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 所需的迭代次数。此时,我们的区块链包含四个区块,包括创世区块。

转储整个区块链

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

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 中构建一个区块链项目。在许多方面,您需要为该项目添加更多功能。

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

此外,矿工肯定更喜欢选择手续费最高的交易。同时,您必须确保手续费低或没有手续费的交易不会永远被饿死。

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

结论

本简明教程将帮助您开始创建自己的区块链项目。

要进行完整的区块链项目开发,您可以从比特币源代码中了解更多信息。

对于大型商业或非商业项目,您可以考虑使用以太坊——一个现成的区块链应用程序平台。

广告