您的位置:

使用 Python 构建区块链

在我们开始使用 Python 编程语言构建区块链之前,让我们回到最开始。2008 年,笔名为中本聪的一位(或多位)作者发布了一份白皮书,描述了纯点对点版本的电子现金。除了这种电子现金系统,交易不需要依赖第三方验证来确保每笔交易的安全性。取而代之的是,每一个事务都会被打上时间戳,然后根据哈希值被哈希到一个正在进行的工作证明链中。

因此,什么是哈希和工作证明?我们将在下面的教程中介绍这些概念,并了解它们如何为加密电子现金系统或加密货币奠定基础。中本聪在他(或她)的论文中描述的电子货币的特殊形式成为比特币,第一种加密货币。然而,当试图使用 Python 构建一个区块链时,这有什么用呢?

在接下来的教程中,我们也会理解同样的道理。

理解区块链

比特币所依赖的系统——一个不断增长的相互连接的记录(即区块)列表——被称为区块链。比特币是这一系统的第一个成功应用,在名声大噪后不久,其他加密货币也建立在同样的信念上。然而,这个系统并不局限于收集财务信息。相反,存储的数据类型无关紧要,并且独立于区块链网络。

从根本上说,存储在区块链中的数据应该包含以下特征:

  1. 不变的
  2. 分布式的
  3. 持久(无数据丢失)
  4. 不可破解

区块链是一个开放源代码的应用,在成千上万台计算机之间共享。这些计算机遵循一套规则,以便追踪从与区块链软件相关的账户发送的资金。为了维护区块链的完整性和交易发生时的网络安全性,这些质量是强制性的。

每个区块都是一组数据,例如,“汤姆在 14 日星期二付给哈利 500 美元。”在区块链上,我们可以不用银行汇款。为了说明这种系统的简单和优雅,并描述细微差别,我们将了解使用 Python 编程语言创建我们自己的区块链的过程。

对于下面的项目,我们只需要 Python。此外,请记住,我们的区块链将是一个简化的高级介绍。我们不会建造一个完整的比特币区块链。相反,我们将创建函数来添加块、事务和加密,这样我们的数据就不会被篡改。

我们开始吧。

用 Python 构建区块链

为了更好地理解,我们把建设区块链的过程分成了几个步骤。这些步骤如下:

步骤 1: 创建区块链类

步骤 2: 编写一个函数来构建新的块

步骤 3: 编写函数来创建新事务并获取最后一个块

步骤 4: 编写一个函数来“哈希”块

步骤 5: 创建新的区块链并发送一些钱

现在,我们将在以下部分讨论这些步骤。

创建区块链类

我们将从导入所需的库开始。在这种情况下,我们将需要 hashlib 库来加密,需要 JSON 库来格式化我们的块,需要时间库来存储每个块的时间戳。然后我们将创建一个类并初始化以下变量:

  1. 链:这将是一个空列表,我们将向其中添加区块。确切地说,是“区块链”。
  2. pendingTransactions :当用户互相发送硬币时,他们的交易将位于这个数组中,直到我们批准并将它们插入到一个新的块中。
  3. 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 对象:

  1. 指数:取区块链的长度,加 1。我们将使用它来引用单个块,例如,genesis 块的索引= 1。
  2. 时间戳:在时间()模块的帮助下,我们会在块创建的时候给它打上时间戳。用户现在可以检查他们的交易何时在链上被确认。
  3. 交易:任何已经在‘待处理’列表中的交易都将显示在新的区块中。
  4. 证明:这个属性来自于矿工认为自己找到了有效的‘证明’或者‘现时’。
  5. previous_hash :最近批准的块的哈希版本。

一旦我们将上述属性添加到新块中,我们将把它们包括在链中。最初,我们清空待处理的事务列表,然后将新的块添加到自身链中并返回它。

让我们使用下面显示的代码片段来理解上面的内容。

示例:


# 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 函数,并包含了前面描述的属性。我们清空了待处理的事务列表,并向链中添加了新的块。最后,我们归还了新的街区。

编写函数来创建新事务并获取最后一个块

现在,让我们将事务列表包含在程序中,因为没有事务,整个程序毫无意义。因此,让我们首先定义一个方法,该方法返回最近添加的块(我们将在一秒钟内将它用于新索引)。

之后,我们将创建另一个方法来表示新的事务。该方法将由三个最重要的变量组成- 发送方、接收方和金额。如果每笔交易都不包含这些变量,用户就不能用新生产的加密货币消费、赚钱或买东西。请记住,这些交易过于简化,不能反映真正的加密货币中可能存在的东西。

我们将把事务处理对象包含到事务处理对象池中。这些将保持不确定状态,直到一个新的区块被开采并加入区块链。为了将来参考,我们将返回新事务将要添加到的块的索引。

让我们考虑下面的代码片段来证明这一点。

示例:


#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() ,它返回最近添加的块。然后,我们将该函数定义为新交易(),其中我们将 JSON 对象定义为该 _ 交易,并包括发送者、接收者和金额的地址。我们将这个 JSON 对象添加到挂起的事务中,并返回最后一个块。

编写一个函数来“哈希”块

现在,让我们将密码学添加到程序中。正如我们所知,比特币和许多其他区块链货币利用 SHA-256 加密哈希函数,该函数接受一些文本字符串(存储为 Unicode 值)并吐出 64 个字符长的加密字符串。在区块链,我们加密的文本被认为是一个块。例如,比特币成因块的加密字符串或“哈希”如下所示:

FBC 13 b85 C4 ade 52 E2 def 26 EAE 950 F3 b55 df 887 ad 0 F0 FB 5 ebfd 5681 F7 fcb

区块链被认为是防篡改的,因为每个块都由该块的先前哈希的副本组成。由于新的哈希是从最后一个块中派生出来的,我们不能在不改变它前面的每个哈希的情况下改变一个块的任何方面。

假设有人将比特币区块链下载到他们的电脑上,并写道“Satoshi 向 Alex 发送了 723.6 万枚比特币!”进入创世纪区块,并向比特币网络广播了这一消息,并声称他是一名秘密亿万富翁。然而,只要任何自尊的矿工将他们当前的区块链副本,尤其是存储在每个区块中的哈希值,与他的链副本进行比较,他们就会注意到他是一个骗子,拒绝验证它并让他离开网络。

我们将定义接受新块并将它的键/值对变成字符串的方法。然后,我们将该字符串转换为 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

说明:

在上面的代码片段中,我们定义了哈希()函数,并接受一个块,将它们转换为字符串,然后转换为 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,没有事务),此外还有我们自己添加的 2。我们还可以注意到,加密的哈希(从每个前面的块中导出)和时间戳彼此不匹配。诚然,当我们几乎同时执行程序和生成块时,计算机几乎同时构建每个块;然而,比特币块大约每十分钟创建一次。

我们有人注意到账户余额了吗?区块链不是银行,这里有一个很好的例子来区分两者。一个加密货币钱包将通过扫描完整的链并总结我们收到和花费了多少硬币来估计余额。我们不需要依靠银行来告诉我们账户上的金额。我们只信任网络,而不是一个大公司。是不是很迷人?

总结

在下面的教程中,我们已经成功地构建了一个区块链,我们可以用充满加密货币交易的块来填充它;然而,这不是一个安全的网络。首先,我们创建了一个任何时候有人调用 newBlock() 的块,并且没有条件。 newBlock() 方法需要一个名为证明的参数;然而,在我们的情况下,这可能是任何事情。它可以是一个数字或一串“你好,世界”,或者字面上的任何东西。

在比特币的网络中,一个叫做工作证明的地方有一个共识机制,它说明了实现安全的规则。证据是一个很难找到的随机数,除非我们有一些专门的高性能机器昼夜不停地工作。

我们还缺少许多其他细节,例如矿工收取的费用、交易计数、公钥/私钥、Merkle 树结构等等。然而,作为区块链中运动线段的基本示例,上面的演练对我们很有帮助。