Python Blockchain 简明教程
Python Blockchain - Introduction
在区块链教程中,我们详细学习了区块链背后的理论知识。区块链是支撑全球最流行的数字货币 Bitcoin 的基石。本教程深入探讨了 Bitcoin 的复杂性,并全面解释了区块链架构。下一步是构建我们自己的区块链。
中本聪创造了世界上第一个名为 Bitcoin 的虚拟货币。受 Bitcoin 成功的影响,许多其他人创建了自己的虚拟货币。举几个例子:莱特币、Zcash 等。
现在,您可能也想发行自己的货币。我们将此称为 TPCoin(TutorialsPoint 硬币)。您将编写一个区块链来记录所有涉及 TPCoin 的交易。TPCoin 可用于购买比萨饼、汉堡、沙拉等。可能还有其他服务提供商加入您的网络,并开始接受 TPCoin 作为提供其服务的货币。可能性是无穷无尽的。
在本教程中,让我们了解如何构建这样一个系统并在市场上发行您自己的数字货币。
Client
客户是指从其他供应商处购买商品的人。客户自己可能会成为一个供应商,并会收取他人对其提供的商品的钱。我们在此假定客户既可以是 TPCoin 的供应商,也可以是接收者。因此,我们将在我们的代码中创建一个客户端类,该类具有发送和接收货币的能力。
Miner
矿工是指从交易池中提取交易并将它们组装到一个块中的人。矿工必须提供有效的功耗证明才能获得挖矿奖励。矿工收取的所有费用都归他所有。他可以用这笔钱从网络上其他注册的供应商处购买商品或服务,就像上面描述的客户一样。
Blockchain
最后,区块链是一个数据结构,按时间顺序链接所有已挖掘的块。此链是不可变的,因此是防篡改的。
您可以通过在新的 Jupyter 笔记本中键入每一步中提供的代码来遵循本教程。或者,您可以从 www.anaconda.com 下载整个 Jupyter 笔记本。
在下一章中,我们将开发一个使用我们区块链系统的客户端。
Python Blockchain - Developing Client
客户端是指持有 TPCoin 并与网络上的其他供应商(包括他自己的供应商)进行商品/服务交易的人。我们应该为此目的定义一个 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 Blockchain - Client Class
Client 类使用内置 Python RSA 算法生成 private 和 public 密钥。有兴趣的读者可以参考 this tutorial 以了解 RSA 的实现。在对象初始化期间,我们创建私钥和公钥,并将它们的值存储在实例变量中。
self._private_key = RSA.generate(1024, random)
self._public_key = self._private_key.publickey()
请注意,您绝不能丢失您的私钥。为了记录,生成的私钥可以复制到安全的外置存储设备上,或者您也可以简单地在纸上写下它的 ASCII 表示。
生成的 public 密钥将用作客户的身份。为此,我们定义一个名为 identity 的属性,它返回公钥的 HEX 表示。
@property
def identity(self):
return
binascii.hexlify(self._public_key.exportKey(format='DER'))
.decode('ascii')
identity 对每个客户都是唯一的,并且可以公开。任何人都可以使用此 identity 向您发送虚拟货币,它将添加到您的钱包中。
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')
Testing Client
现在,我们将编写代码来说明如何使用 Client 类 -
Dinesh = Client()
print (Dinesh.identity)
上述代码创建了一个 Client 实例,并将其赋值给变量 Dinesh 。我们通过调用其 identity 方法来打印 Dinesh 的公钥。输出如下 −
30819f300d06092a864886f70d010101050003818d0030818902818100b547fafceeb131e07
0166a6b23fec473cce22c3f55c35ce535b31d4c74754fecd820aa94c1166643a49ea5f49f72
3181ff943eb3fdc5b2cb2db12d21c06c880ccf493e14dd3e93f3a9e175325790004954c34d3
c7bc2ccc9f0eb5332014937f9e49bca9b7856d351a553d9812367dc8f2ac734992a4e6a6ff6
6f347bd411d07f0203010001
现在,让我们继续在下一章中创建一笔交易。
Python Blockchain - Transaction Class
在本章中,让我们创建一个 Transaction 类,以便客户端能够向某人汇款。请注意,客户端既可以是发件人,也可以是收件人。当您希望收款时,其他发件人将创建一个交易并在其内指定您的 public 地址。我们定义一个交易类的初始化如下 −
def __init__(self, sender, recipient, value):
self.sender = sender
self.recipient = recipient
self.value = value
self.time = datetime.datetime.now()
init 方法采用三个参数 − 发件人的 public 密钥,收件人的 public 密钥以及要发送的金额。这些存储在实例变量中以供其他方法使用。此外,我们还创建了一个用于存储交易时间的变量。
接下来,我们编写一个名为 to_dict 的实用程序方法,该方法将上述四个实例变量组合在一个字典对象中。这仅仅是为了将整个交易信息放在一个变量中访问。
您从前面的教程中知道,区块链中的第一个块是 Genesis 块。Genesis 块包含区块链创建者发起的第一次交易。此人的身份可能像比特币的情况一样保密。因此,当创建第一次交易时,创建者可能会将他的身份发送为 Genesis 。因此,在创建字典时,我们会检查发件人是否为 Genesis ,如果为真,则我们只为身份变量分配某个字符串值;否则,我们将发件人的身份分配给 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})
最后,我们将使用发件人的私钥对这个字典对象签名。和以前一样,我们使用内置的具有 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 类。
Testing Transaction Class
为此,我们将创建两个用户,称为 Dinesh 和 Ramesh 。Dinesh 将向 Ramesh 发送 5 TPCoin。为此,我们首先创建名为 Dinesh 和 Ramesh 的客户端。
Dinesh = Client()
Ramesh = Client()
请记住,当您实例化 Client 类时,将创建客户端唯一的 public and 私钥。由于 Dinesh 要向 Ramesh 发送付款,因此他将需要使用客户端的身份属性获取 Ramesh 的公钥。
因此,我们将使用以下代码创建交易实例 −
t = Transaction(
Dinesh,
Ramesh.identity,
5.0
)
请注意,第一个参数是发件人,第二个参数是收件人的公钥,第三个参数是要转账的金额。 sign_transaction 方法从第一个参数中检索发件人的私钥以签署交易。
交易对象创建后,您将通过调用其 sign_transaction 方法对其进行签名。此方法以可打印的格式返回生成的签名。我们使用以下两行代码生成并打印签名 −
signature = t.sign_transaction()
print (signature)
当您运行上述代码时,您将看到类似于此的输出 −
7c7e3c97629b218e9ec6e86b01f9abd8e361fd69e7d373c38420790b655b9abe3b575e343c7
13703ca1aee781acd7157a0624db3d57d7c2f1172730ee3f45af943338157f899965856f6b0
0e34db240b62673ad5a08c8e490f880b568efbc36035cae2e748f1d802d5e8e66298be826f5
c6363dc511222fb2416036ac04eb972
现在,由于我们创建客户端和交易的基本基础设施已经就绪,我们现在将像现实生活中一样执行多个客户端和多个交易。
Creating Multiple Transactions
由多个客户端进行的交易在系统中排队;矿工从这个队列中选取交易,并将其添加到区块。然后,他们将挖掘区块,获胜的矿工有权将区块添加到区块链,从而为自己赚取一些钱。
当我们讨论区块链的创建时,稍后我们将描述这个挖掘过程。在我们编写用于多笔交易的代码之前,让我们添加一个小的实用的函数来打印给定交易的内容。
Displaying Transaction
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 ('-----')
接下来,我们定义一个事务队列来存储我们的事务对象。
Transaction Queue
若要创建一个队列,我们声明一个名为 transactions 的全局 list 变量,如下所示 −
transactions = []
我们将简单地把每个新创建的事务附加到此队列。请注意,为了简洁,我们不会在本教程中实现队列管理逻辑。
Creating Multiple Clients
现在,我们将开始创建事务。首先,我们将创建四个客户端,他们将彼此发送资金,以从其他方获取各种服务或商品。
Dinesh = Client()
Ramesh = Client()
Seema = Client()
Vijay = Client()
此时,我们有四个客户端,分别称为 Dinesh、Ramesh、Seema 和 Vijay。我们目前假设,这些客户端每人都持有钱包中的 TPCoin,以便进行交易。这些客户端每个人的身份都将通过使用这些对象的 identity 属性来指定。
Creating First Transaction
现在,我们启动我们的第一个事务,如下所示 −
t1 = Transaction(
Dinesh,
Ramesh.identity,
15.0
)
在此事务中,Dinesh 向 Ramesh 发送 5 个 TPCoin。为了使事务成功,我们将不得不确保 Dinesh 钱包中有足够的资金用于此付款。请注意,我们需要一个创世事务来启动系统中的 TPCoin 流通。您将很快在阅读的同时编写此创世事务的事务代码。
我们将使用 Dinesh 的私钥对该事务进行签名,并将其添加到事务队列,如下所示 −
t1.sign_transaction()
transactions.append(t1)
在 Dinesh 进行第一个交易之后,我们将在上面创建的不同客户端之间创建更多交易。
Adding More Transactions
我们现在将创建更多的事务,每个事务向另一方提供几枚 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)
当您运行以上代码时,队列中将有十笔交易供矿工创建区块。
Dumping Transactions
作为区块链管理器,您可能定期查看交易队列的内容。为此,您可以使用我们先前开发的 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 Blockchain - Block Class
一个区块包含数量不等的事务。为了简单起见,在我们案例中,我们将假设区块包含固定数量的事务,在本例中为三个。由于该区块需要存储这三个事务的列表,因此,我们将声明一个名为 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 Blockchain - Creating Genesis Block
我们假设 TPCoins 的发起者最初向已知客户 Dinesh 发放了 500 个 TPCoins。为此,他首先创建了一个 Dinesh 实例 −
Dinesh = Client()
然后,我们创建一个创世交易,并将 500 个 TPCoins 发送到 Dinesh 的公钥地址。
t0 = Transaction (
"Genesis",
Dinesh.identity,
500.0
)
现在,我们创建一个 Block 类实例,并将它命名为 block0 。
block0 = Block()
我们初始化 previous_block_hash 和 Nonce 实例变量为 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 Creating Blockchain
区块链包含一个相互链接的区块列表。为了存储整个列表,我们将创建一个称为 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 Blockchain - Adding Genesis Block
向区块链中添加一个区块包括将创建的区块追加到我们的 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 Blockchain - Creating Miners
为了启用挖矿,我们需要开发一个挖矿函数。挖矿功能需要针对一个给定的字符串生成一个摘要,并提供一个工作证明。让我们在本章讨论这些内容。
Message Digest Function
我们将编写一个名为 sha256 的实用程序函数,用于针对一个给定的字符串创建摘要——
def sha256(message):
return hashlib.sha256(message.encode('ascii')).hexdigest()
sha256 函数采用一个 message 作为参数,将其编码成 ASCII 码,生成一个十六进制摘要,并将值返回给调用方。
Mining Function
现在我们开发一个实现了我们自己挖矿策略的 mine 函数。我们在这种情况下的策略是针对给定的消息生成一个哈希,该哈希以给定的 1 的个数作为前缀。给定的 1 的个数指定为 mine 函数的参数,其指定为难度级别。
例如,如果你指定一个难度级别为 2,则针对给定消息生成的哈希应当以两个 1 开始——比如 11xxxxxxxx。如果难度级别为 3,则生成的哈希应当以三个 1 开始——比如 111xxxxxxxx。考虑到这些要求,我们现在将按照以下给定的步骤来开发挖矿函数。
Step 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 语句,它在返回调用函数之前会打印摘要值和满足条件前需要的迭代次数。
Testing Mining Function
要测试挖掘函数,只需执行以下语句 −
mine ("test message", 2)
当运行以上代码时,看到的输出将与以下内容类似 −
after 138 iterations found nonce:
11008a740eb2fa6bf8d55baecda42a41993ca65ce66b2d3889477e6bfad1484c
请注意,生成的摘要以 “11” 开头。如果难度等级更改为 3,生成的摘要将以 “111” 开头,而且当然需要更多的迭代次数了。正如你所见,处理能力越强的矿工越早就能挖到给定的消息。矿工们之间就是以这种方式竞争,以赚取收益。
现在,我们可以为区块链添加更多的区块了。让我们在下一章学习。
Python Blockchain - Adding Blocks
每个矿工都会从先前创建的事务池中选取事务。为了追踪已挖掘的消息数量,我们必须创建一个全局变量 −
last_transaction_index = 0
我们现在让第一个矿工向区块链添加一个区块。
Adding First Block
要添加新区块,我们首先创建一个 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
Adding More Blocks
我们现在将向我们的区块链添加两个区块。下面给出了添加下一个两个区块的代码 -
# 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 个区块,包括创世区块。
Dumping Entire Blockchain
你可以使用以下语句验证整个区块链的内容 -
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 Blockchain - Scope and Conclusion
在本教程中,我们学习了如何在 Python 中构造区块链项目。有很多地方需要你向这个项目添加更多功能。
例如,你需要编写函数来管理交易队列。在交易被挖掘并且挖掘的区块被系统接受后,它们就不需要再存储了。
另外,矿工肯定会喜欢选择费用最高的交易。与此同时,你必须确保具有低费用或无费用的交易不会永远耗尽。
你需要开发用于管理队列的算法。此外,目前的教程不包括客户端界面代码。你需要为普通客户端和矿工开发这个代码。一个成熟的区块链项目会包含更多代码,并且超出了本教程的范围。感兴趣的读者可以下载 bitcoin source 进行进一步学习。
Conclusions
本教程应该可以让你开始创建自己的区块链项目。
如需进行成熟的区块链项目开发,你可以从 bitcoin source 了解更多信息。
对于较大的商业或非商业项目,你可以考虑使用 Ethereum - 一个随时可用的区块链应用程序平台。