使用Python构建区块链
在开始使用Python编程语言构建区块链之前,让我们回到最开始。在2008年,一个以笔名Satoshi Nakamoto为首的作者(或者是多位作者)发布了一篇白皮书,描述了一种纯粹的点对点电子现金系统。这个电子现金系统独有之处在于,交易不需要依赖第三方验证来确保每一笔交易的安全性。相反,每笔交易都会被时间戳标记,然后基于哈希计算生成一个持续的工作证明链。
那么,哈希和工作证明是什么?我们将在接下来的教程中介绍这些概念,并了解它们如何为加密的电子现金系统或加密货币奠定基础。Satoshi Nakamoto在他(或她)的论文中描述的这种特定形式的电子货币被称为比特币,是第一种加密货币。然而,在尝试使用Python构建区块链时,这有什么用呢?
下面的教程中,我们将理解这一点。
理解区块链
比特币所依赖的系统 – 一个不断增长的相互连接的记录列表(即区块链) – 被称为区块链。比特币是这个系统的第一个成功应用,它的声望不断增长后,其他加密货币也在相同的理念下建立起来。然而,这个系统不仅局限于收集金融信息。存储的数据类型实际上与区块链网络无关。
基本上,存在于区块链中的数据应该具备以下特点:
- 不可变
- 分布式
- 持久(不丢失数据)
- 不可被黑客攻击
区块链是一个开源应用程序,它在数千台计算机之间共享。这些计算机遵循一系列规则来追踪与区块链软件相关的账户发送的货币。这些特性是必须的,以维护区块链的完整性和交易发生的网络安全。
每个区块是一组数据,例如,“Tom于14日星期二支付了Harry $500。”在区块链上,我们可以在不使用银行的情况下发送货币。为了说明这种系统的简洁和优雅,并描述其中的细微差别,我们将了解使用Python编程语言创建自己的区块链的过程。
在接下来的项目中,我们只需要Python。同时,请记住我们的区块链将是一个简化的、高层次的介绍。我们不会构建一个完整的比特币区块链,而是将创建一些函数来添加区块、交易和加密,以使我们的数据具有防篡改的特性。
让我们开始吧。
使用Python构建区块链
我们将构建区块链的过程分为几个步骤以更好地理解。这些步骤如下:
步骤1: 创建一个区块链类
步骤2: 编写一个构建新区块的函数
步骤3: 编写创建新交易和获取最新区块的函数
步骤4: 编写一个将区块“哈希化”的函数
步骤5: 创建一个新的区块链并发送一些货币
现在,我们将在下面的几节中讨论这些步骤。
创建一个区块链类
我们将首先导入所需的库。在这种情况下,我们将需要 hashlib 用于加密, JSON 库用于格式化我们的区块,和 time 库用于每个区块的时间戳。然后我们将创建一个类并初始化以下变量:
- chain :这将是一个空列表,我们将向其中添加区块。简而言之,它是’区块链’。
- pendingTransactions :当用户彼此发送硬币时,他们的交易将在此数组中等待,直到我们批准并将它们插入到新的区块中。
- newBlock :这是一个我们将很快定义的方法,并且我们将使用它来包含每个区块在链中。
让我们考虑以下代码片段,演示了相同的情况。
示例:
# importing the required libraries
import hashlib
import json
from time import time
# creating the Block_chain class
class Block_chain(object):
def __init__(self):
self.chain = []
self.pendingTransactions = []
self.newBlock(previousHash = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks.", the_proof = 100)
解释:
在上面的代码片段中,我们导入了所需的库并创建了 Block_chain 类,其中初始化了前面描述的不同变量。
编写一个函数来构造新的区块
现在我们已经初始化了一个空的区块链,让我们开始向其中插入区块。然后我们将定义具有以下属性的JSON对象:
- index :取区块链的长度并加1。我们将使用它来引用单个区块,所以例如,创世区块的index = 1。
- timestamp :通过 time() 模块的帮助,我们将在创建区块时标记。现在用户可以检查他们的交易何时在链上确认。
- transactions :在“待处理”列表中的任何交易都将显示在新的区块中。
- proof :这个属性来自认为自己找到了有效的“proof”或“nonce”的矿工。
- previous_hash :最近一个已批准的区块的哈希版本。
一旦我们将上述属性添加到新的区块中,我们将把它们包含在链中。最初,我们清空待处理交易列表,然后将新的区块添加到 self.chain 并返回它。
让我们通过下面显示的代码片段来理解上面的内容。
示例:
# Creating a new block listing key/value pair of
# block information in a JSON object.
# Reset the list of pending transactions &
# append the newest block to the chain.
def newBlock(self, the_proof, previousHash = None):
the_block = {
'index': len(self.chain) + 1,
'timestamp': time(),
'transactions': self.pendingTransactions,
'proof': the_proof,
'previous_hash': previousHash or self.hash(self.chain[-1]),
}
self.pendingTransactions = []
self.chain.append(the_block)
return the_block
解释:
在以上代码片段中,我们定义了newBlock
函数并包含了前面描述的属性。我们清空了待处理的交易列表,并将新块添加到链中。最后,我们返回了新块。
编写创建新交易和获取最后一个区块的函数
现在,让我们在程序中包含交易列表,因为没有交易列表,整个程序就没有意义。所以,我们首先定义一个方法来返回最近添加的区块(我们将在之后使用它来获取新索引)。
之后,我们将创建另一个方法来表示一个新的交易。该方法将包含三个最重要的变量 – the_sender
、the_recipient和
the_amount`。如果不在每个交易中包含这些变量,用户就无法用新生产的加密货币花费、赚取或购买物品。请记住,这些交易过于简化,不反映真实加密货币中可能出现的情况。
我们将把the_transaction
JSON对象加入到 pendingTransactions 池中。这些交易将保持不确定状态,直到挖掘出一个新区块并添加到区块链中。并且为了将来参考,我们将返回新交易要添加到的区块的索引。
让我们考虑下面的示例代码:
示例:
#Searching the blockchain for the most recent block.
@property
def lastBlock(self):
return self.chain[-1]
# Adding a transaction with relevant info to the 'blockpool' - list of pending transactions.
def newTransaction(self, the_sender, the_recipient, the_amount):
the_transaction = {
'sender': the_sender,
'recipient': the_recipient,
'amount': the_amount
}
self.pendingTransactions.append(the_transaction)
return self.lastBlock['index'] + 1
解释:
在上面的代码片段中,我们定义了方法为 lastBlock() ,它返回最近添加的块。然后,我们定义了函数为 newTransaction() ,在其中我们定义了JSON对象为 the_transaction ,并包含了发送者、接收者和金额的地址。我们将该JSON对象添加到 pendingTransaction 并返回最后一个块。
编写一个函数来“散列”块
现在,让我们为程序添加密码学。我们知道,比特币和许多其他区块链使用SHA-256,这是一个加密哈希函数,它接受一些文本字符串(存储为Unicode值)并输出一个64个字符长的加密字符串。在区块链中,我们要加密的文本被视为一个块。例如,比特币创世块的加密字符串或“哈希”看起来像这样:
fbc13b85c4ade52e2def26eae950f3b55b174df887ad0f0fb5ebfd56881f7fcb
区块链被认为是防篡改的,因为每个块都包含前一个块的哈希的副本。而且由于新的哈希是从上一个块派生的,所以我们不能改变块的任何方面,而不改变其前面的每个哈希。
假设有人将比特币区块链下载到他们的计算机上,并在创世块中写下“Satoshi sends Alex 7,236,000 Bitcoin!”并将其广播到比特币网络,并声称他是一个秘密的亿万富翁。然而,只要任何自尊的矿工将他们当前的区块链副本,尤其是存储在每个块中的哈希值,与他的链副本进行比较,他们就会注意到他是个骗子,拒绝对其进行验证并将其赶出网络。
我们将定义一个方法接受新块,并将其键/值对修改为字符串。然后,我们将将该字符串转换为Unicode,将其传递给 hashlib 库的 SHA256 方法,并从其返回值创建一个十六进制字符串。然后,我们将返回新的哈希值。
让我们使用下面的代码片段来理解这一点。
示例:
# receiving one block. Turning it into a string, turning that into
# Unicode (for hashing). Hashing with SHA256 encryption,
# then translating the Unicode into a hexadecimal string.
def hash(self, the_block):
stringObject = json.dumps(the_block, sort_keys = True)
blockString = stringObject.encode()
rawHash = hashlib.sha256(blockString)
hexHash = rawHash.hexdigest()
return hexHash
说明:
在上述代码片段中,我们定义了 hash() 函数,它接受一个块并将它们转换为字符串,然后转换为Unicode进行哈希。然后,我们使用 SHA256() 函数进行加密,然后将Unicode转换为十六进制字符串。
创建一个新的区块链并发送一些货币
由于我们为区块链创建了一个类,并添加了各种方法以构建新的区块和新的交易,以及一个用于使用SHA256加密哈希任何块的自定义方法,让我们开始构建链。
我们将初始化一个 Block_chain 类的实例,并执行一些虚拟交易。确保将它们列在我们包含在链中的一些块中。
让我们考虑以下演示相同的代码片段。
示例:
block_chain = Block_chain()
transaction1 = block_chain.newTransaction("Satoshi", "Alex", '10 BTC')
transaction2 = block_chain.newTransaction("Alex", "Satoshi", '2 BTC')
transaction3 = block_chain.newTransaction("Satoshi", "James", '10 BTC')
block_chain.newBlock(10123)
transaction4 = block_chain.newTransaction("Alex", "Lucy", '2 BTC')
transaction5 = block_chain.newTransaction("Lucy", "Justin", '1 BTC')
transaction6 = block_chain.newTransaction("Justin", "Alex", '1 BTC')
block_chain.newBlock(10384)
print("Genesis block: ", block_chain.chain)
说明:
我们在上面的代码片段中实例化了 Block_chain() 类。然后我们执行了一些交易并将它们打印给用户。
现在,让我们来看一下使用Python构建区块链的完整代码。
完整项目代码
文件:buildingBlockchain.py
# importing the required libraries
import hashlib
import json
from time import time
# creating the Block_chain class
class Block_chain(object):
def __init__(self):
self.chain = []
self.pendingTransactions = []
self.newBlock(previousHash = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks.", the_proof = 100)
# Creating a new block listing key/value pairs of
# block information in a JSON object.
# Reset the list of pending transactions &
# append the newest block to the chain.
def newBlock(self, the_proof, previousHash = None):
the_block = {
'index': len(self.chain) + 1,
'timestamp': time(),
'transactions': self.pendingTransactions,
'proof': the_proof,
'previous_hash': previousHash or self.hash(self.chain[-1]),
}
self.pendingTransactions = []
self.chain.append(the_block)
return the_block
#Searching the blockchain for the most recent block.
@property
def lastBlock(self):
return self.chain[-1]
# Adding a transaction with relevant info to the 'blockpool' - list of pending tx's.
def newTransaction(self, the_sender, the_recipient, the_amount):
the_transaction = {
'sender': the_sender,
'recipient': the_recipient,
'amount': the_amount
}
self.pendingTransactions.append(the_transaction)
return self.lastBlock['index'] + 1
# receiving one block. Turning it into a string, turning that into
# Unicode (for hashing). Hashing with SHA256 encryption,
# then translating the Unicode into a hexidecimal string.
def hash(self, the_block):
stringObject = json.dumps(the_block, sort_keys = True)
blockString = stringObject.encode()
rawHash = hashlib.sha256(blockString)
hexHash = rawHash.hexdigest()
return hexHash
block_chain = Block_chain()
transaction1 = block_chain.newTransaction("Satoshi", "Alex", '10 BTC')
transaction2 = block_chain.newTransaction("Alex", "Satoshi", '2 BTC')
transaction3 = block_chain.newTransaction("Satoshi", "James", '10 BTC')
block_chain.newBlock(10123)
transaction4 = block_chain.newTransaction("Alex", "Lucy", '2 BTC')
transaction5 = block_chain.newTransaction("Lucy", "Justin", '1 BTC')
transaction6 = block_chain.newTransaction("Justin", "Alex", '1 BTC')
block_chain.newBlock(10384)
print("Genesis block: ", block_chain.chain)
输出:
Genesis block: [
{'index': 1,
'timestamp': 1640067926.584454,
'transactions': [],
'proof': 100,
'previous_hash': 'The Times 03/Jan/2009 Chancellor on brink of second bailout for banks.'},
{'index': 2,
'timestamp': 1640067926.584454,
'transactions': [
{'sender': 'Satoshi', 'recipient': 'Alex', 'amount': '10 BTC'},
{'sender': 'Alex', 'recipient': 'Satoshi', 'amount': '2 BTC'},
{'sender': 'Satoshi', 'recipient': 'James', 'amount': '10 BTC'}
],
'proof': 10123,
'previous_hash': 'a1b0cf063d43989421eb4b28d9be8f82c2e2e9e40bc9814321e3cbb70b00530a'},
{'index': 3,
'timestamp': 1640067926.584454,
'transactions': [
{'sender': 'Alex', 'recipient': 'Lucy', 'amount': '2 BTC'},
{'sender': 'Lucy', 'recipient': 'Justin', 'amount': '1 BTC'},
{'sender': 'Justin', 'recipient': 'Alex', 'amount': '1 BTC'}
],
'proof': 10384,
'previous_hash': '23699917fdcc013a85bbb5872251768e976bfcc2cd8403565c04970bca24a871'}
]
解释:
从上述输出中,我们可以观察到我们的区块链当前包含三个区块:创世区块(索引为1且没有交易),以及我们自己添加的两个区块。我们还可以注意到加密哈希值(来自每个前一区块)和时间戳不匹配。尽管计算机几乎同时构建了每个区块,我们执行程序并几乎同时生成区块,但是比特币区块大约每十分钟创建一个。
我们中有人注意到任何账户余额吗?区块链不是银行,这是一个很好的示例来区分二者。加密货币钱包将通过扫描完整的链条并总结我们收到和支出了多少硬币来估计余额。我们不必依赖银行告诉我们账户中的金额,我们只是相信网络而不是一个巨型公司。这难道不是很有趣吗?
总结
在接下来的教程中,我们成功构建了一个可以填充包含加密货币交易的区块的区块链;然而,这不是一个安全的网络。首先,每当有人调用 newBlock() 时,我们就创建一个区块,并且没有条件。在我们的情况下, newBlock() 方法需要一个称为proof的参数,但是这个参数在我们的情况下可以是任何东西。它可以是一个数字或字符串,比如“hello world”,或者真的是任何东西。
在比特币网络中,有一个被称为工作量证明的共识机制,它说明了安全性的规则。证明是一个随机数,除非有一些全天候运行的专门高性能机器,否则很难找到。
还有许多其他细节我们没有提到,比如矿工收集费用、交易数量、公钥/私钥、Merkle树结构等。然而,上述演示对我们来说是一个基本的区块链移动部分的示例,对我们很有帮助。