
- Python 區塊鏈教程
- Python 區塊鏈 - 首頁
- Python 區塊鏈 - 簡介
- 區塊鏈 - 開發客戶端
- 區塊鏈 - 客戶端類
- 區塊鏈 - 交易類
- 建立多筆交易
- 區塊鏈 - 區塊類
- 區塊鏈 - 建立創世塊
- 區塊鏈 - 建立區塊鏈
- 區塊鏈 - 新增創世塊
- 區塊鏈 - 建立礦工
- 區塊鏈 - 新增區塊
- 區塊鏈 - 範圍與結論
- Python 區塊鏈資源
- Python 區塊鏈 - 快速指南
- Python 區塊鏈 - 資源
- Python 區塊鏈 - 討論
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')
我們現在將測試這個交易類。
測試交易類
為此,我們將建立兩個使用者,稱為Dinesh和Ramesh。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_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 建立區塊鏈
區塊鏈包含一個彼此連結的區塊列表。為了儲存整個列表,我們將建立一個名為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 中構建一個區塊鏈專案。在許多方面,您需要為該專案新增更多功能。
例如,您需要編寫用於管理交易佇列的函式。交易被挖掘並且挖掘的區塊被系統接受後,就不需要再儲存它們了。
此外,礦工肯定更喜歡選擇手續費最高的交易。同時,您必須確保手續費低或沒有手續費的交易不會永遠被餓死。
您需要開發用於管理佇列的演算法。此外,當前教程不包含客戶端介面程式碼。您需要為普通客戶端和礦工開發此程式碼。完整的區塊鏈專案將包含更多程式碼行,超出了本教程的範圍。感興趣的讀者可以下載比特幣原始碼以進行進一步研究。
結論
本簡明教程將幫助您開始建立自己的區塊鏈專案。
要進行完整的區塊鏈專案開發,您可以從比特幣原始碼中瞭解更多資訊。
對於大型商業或非商業專案,您可以考慮使用以太坊——一個現成的區塊鏈應用程式平臺。