看完此文再不懂区块链算我输:手把手教你用Python从零开始创建区块链
導(dǎo)讀:如果你還沒(méi)有聽(tīng)說(shuō)過(guò) 3 點(diǎn)鐘區(qū)塊鏈群,說(shuō)明你還不是鏈圈的人;如果你還沒(méi)有加入 3 點(diǎn)鐘區(qū)塊鏈群,說(shuō)明你還不是鏈圈的大佬;如果你還沒(méi)有被 3 點(diǎn)鐘區(qū)塊鏈群刷屏,說(shuō)明你還體會(huì)不到什么是“幣圈一天,人間一年”。
“三點(diǎn)鐘區(qū)塊鏈”無(wú)疑成為了大家春節(jié)期間焦慮的根源,而“區(qū)塊鏈”注定是 2018 年被持續(xù)討論、關(guān)注的行業(yè)性熱點(diǎn)話題。
3 月 1 日,朱嘯虎對(duì)正翻涌不斷的區(qū)塊鏈熱潮再次開(kāi)炮,在朋友圈一張畫滿區(qū)塊鏈應(yīng)用的圖上,朱嘯虎質(zhì)疑:所有這些應(yīng)用加在一起,有多少日活用戶?“2000 年的互聯(lián)網(wǎng)泡沫至少還有 eyeball,今天的區(qū)塊鏈除了炒幣外還有什么”?
在此之前,朱嘯虎在朋友圈轉(zhuǎn)發(fā)了諷刺區(qū)塊鏈投資熱的文章《來(lái),喝了這碗?yún)^(qū)塊鏈的毒雞湯!》,并聲明:“不要拉我進(jìn)任何 3 點(diǎn)鐘群,有些風(fēng)口寧愿錯(cuò)過(guò),有些錢寧愿不賺,大家晚節(jié)保重。”朱嘯虎還表示,說(shuō) ICO 是龐氏騙局是在侮辱龐氏騙局。
作為程序員的你,再不懂這個(gè)技術(shù),2018 可能會(huì)被淘汰!下面和小編一起從十個(gè)幽默段子入門區(qū)塊鏈吧!
01 笑噴!區(qū)塊鏈?zhǔn)畟€(gè)段子集錦
1、假如你是一位女性,你男朋友每次跟你說(shuō)一句肉麻的話或者承諾給你買東西,你都立刻錄下來(lái)并且發(fā)給你的和他的所有閨蜜、同學(xué)、同事,還有各種群和朋友圈,讓他再也無(wú)法抵賴,這叫區(qū)塊鏈。
2、麻將是中國(guó)傳統(tǒng)的區(qū)塊鏈項(xiàng)目:四個(gè)礦工一組,先碰撞出 13 個(gè)數(shù)字正確哈希值的礦工可以獲得記賬權(quán)并得到獎(jiǎng)勵(lì)。不可篡改。因?yàn)檎f(shuō)服其他三個(gè)人需要消耗太多算力和體力。
3、玩夜店的小姐姐和玩虛擬幣的小哥哥們有幾處相似:
都是自認(rèn)聰明的優(yōu)秀群體
不給自己賺錢的都是傻 X 屌絲
都認(rèn)識(shí)很多大佬
都明白很多道理
都是在等自己漲價(jià)或者自己的虛擬幣漲價(jià)被別人接盤
4、區(qū)塊鏈?zhǔn)钦?jīng)技術(shù),各種幣正不正經(jīng)就不知道了。
5、吳三桂在山海關(guān)沖冠一怒,本質(zhì)是為了爭(zhēng)奪睡陳圓圓的權(quán)力;大佬們?cè)趨^(qū)塊鏈路上的互懟,本質(zhì)是為了爭(zhēng)奪割韭菜的權(quán)力。
6、新學(xué)期剛開(kāi)始,兒子問(wèn)老爸:「父親工作一欄怎么填?是寫幣民嗎?」老爸猶豫了一下說(shuō):「就寫多家上市公司股東。」
7、最近數(shù)字貨幣很火,很多山寨幣都是幾倍幾十倍的增長(zhǎng)。很多炒幣的開(kāi)始飄飄然,叫囂什么「一幣一嫩模」。有朋友就問(wèn)我要不要跟?我觀點(diǎn)很簡(jiǎn)單:淘金熱,一窩蜂淘金,風(fēng)險(xiǎn)很大。所以,讓他們叫「一幣一嫩模」去吧,不要跟風(fēng)盲目炒幣,我們應(yīng)該賺他們的錢——去當(dāng)嫩模!當(dāng)嫩模!嫩模!
8、我昨天遇見(jiàn)一幣友,問(wèn)他:「近來(lái)幣市暴降,睡覺(jué)質(zhì)量怎么樣?」
他說(shuō):「還行,像嬰兒般睡覺(jué)!」
我說(shuō):「羨慕了。」
他說(shuō):「是睡一個(gè)小時(shí),醒了,然后哭一個(gè)小時(shí),接著,再睡一個(gè)小時(shí),起來(lái)再哭一個(gè)小時(shí)。」
9、老同志語(yǔ)重心長(zhǎng)地對(duì) 80、90 后說(shuō):「別玩那些比特幣,那些虛擬的玩意,做點(diǎn)實(shí)事在北京買個(gè)房、娶個(gè)媳婦,多好!」90 后回答說(shuō):「你們都把幾千塊錢成本的房子搞到10萬(wàn)一平米了。我們不另尋出路,搞一串串?dāng)?shù)字 10 萬(wàn)一個(gè)賣給你們,我們拿什么買得起房子啊?」
10、首先感謝公司拿出價(jià)值 100 萬(wàn)的比特幣作為給員工的獎(jiǎng)勵(lì),其次我覺(jué)得自己很幸運(yùn)能拿到這 95 萬(wàn)的獎(jiǎng)勵(lì),然后我覺(jué)得我還是要好好規(guī)劃一下這 86 萬(wàn)的用處,畢竟 70 萬(wàn)也不是一筆小錢,我打算拿出 20 萬(wàn)給父母,剩下的 36 萬(wàn)暫時(shí)還沒(méi)想好怎么用,總之,感謝公司價(jià)值 30 萬(wàn)的比特幣的獎(jiǎng)勵(lì),謝謝,祝大家和我一樣都能得到這 15 萬(wàn)的獎(jiǎng)勵(lì)。
02 圖解:大白話了解到底區(qū)塊鏈?zhǔn)莻€(gè)啥?
1. what is 區(qū)塊鏈
“區(qū)塊鏈僅僅是一門技術(shù)而已”,“比特幣”僅僅是區(qū)塊鏈技術(shù)的一種應(yīng)用而已, 就好比,一個(gè)人會(huì)廚藝的技術(shù),但是應(yīng)用起他的廚藝可以做出“宮保雞丁”、”魚(yú)香肉絲”等各式的菜肴。
那么,到底“區(qū)塊鏈”是個(gè)啥?我們這里借助網(wǎng)上一個(gè)比較流行的段子,將它用圖形的形式展示給大家。
我們可以將“比特幣”抽象成“某榮的照片”如上圖所示,如果網(wǎng)上很多用戶想要得到某榮的照片,需要到一個(gè)固定的網(wǎng)站去搜索。
當(dāng)然你也沒(méi)其他地方可去嘛,那么好了,這個(gè)某榴網(wǎng)站天天給你彈出廣告啊,小窗口啊,你都得忍著,沒(méi)辦法,因?yàn)榫瓦@一個(gè)地方可以獲取嘛。
再者,這個(gè)網(wǎng)站突然被警察叔叔封殺了咋辦?或者斷電?斷網(wǎng)?管他啥的,反正就是服務(wù)器崩潰了,那么悲劇的“某榮”粉絲們心愛(ài)的 2100 張照片將全部失去提供資源的場(chǎng)所。
這就是網(wǎng)上流傳的新詞“中心化”嘍,他的弊端就是資源集中一起,抗風(fēng)險(xiǎn)容錯(cuò)性很弱。資源容易丟失。
那么,怎么解決這個(gè)問(wèn)題呢?我們?cè)囅?#xff0c;能不能讓每個(gè)“某榮”粉絲,都擁有這 2100 張照片呢?比如下圖:
這樣的話,貌似就不用在依賴那個(gè)“某榴”網(wǎng)站了呢。即使某個(gè)粉絲突然電腦崩潰,他隨便找一個(gè)其他粉絲來(lái)獲得這 2100 張照片,不愁了,不愁了。
后來(lái),一個(gè)叫“某本聰”的一個(gè)虛擬人物提供了,一個(gè)擁有協(xié)議的某榮照片共享文件夾,用戶可以從中獲取照片,但是必須遵循協(xié)議。
這樣,每個(gè)粉絲都可以從這個(gè)文件夾中獲取那 2100 張某榮的照片,但是全部獲取的粉絲必須要遵循一個(gè)協(xié)議,當(dāng)然是人家某本聰定義的協(xié)議啦。
“不得復(fù)制,修改,共享文件中的任意照片,粉絲們?cè)诠蚕砦募A中的任何行為都會(huì)被記錄,并且是按照時(shí)間去記錄!”
粉絲們這么喜愛(ài)某榮,而且不需要去某榴獲取,當(dāng)然就紛紛踴躍加入了~
忽然,有一天,調(diào)皮的小 One 想要違背規(guī)則,在 2018 年 1 月 15 日中午 12 點(diǎn)刪除編號(hào)為 1-100 的某榮照片。
根據(jù)協(xié)議,這個(gè)行為會(huì)被記錄,并且會(huì)廣播給其他粉絲。
照片到底被刪除了么?當(dāng)然不是,因?yàn)樾÷妒掷镆灿姓掌?#xff0c;她收到廣播后可以立刻恢復(fù)共享文件夾中已經(jīng)刪除的照片,小 One 永遠(yuǎn)別想對(duì)“共享文件夾”搞修改破壞,且所有行為都同步記錄在其他用戶的電腦里。
這就是區(qū)塊鏈,數(shù)據(jù)分散存儲(chǔ),去中心化。按時(shí)間戳廣播記錄所有行為,無(wú)法修改、破壞數(shù)據(jù)源或造假。
除非同一時(shí)刻炸掉 100 萬(wàn)個(gè)用戶的電腦,或互聯(lián)網(wǎng)消失,或世界毀滅....否則數(shù)據(jù)將永遠(yuǎn)存在~~
如何增加區(qū)塊鏈保護(hù)的資源?“某本聰”又來(lái)了。他說(shuō),你們是可以在文件中添加某榮照片的,但是呢,你們各位必須達(dá)到某種“共識(shí)”?啥是“共識(shí)”,就是我們都承認(rèn)的規(guī)則嘍。
那到底是個(gè)啥共識(shí)呢?啊,小 One 和小露立刻了解了某本聰?shù)囊馑?#xff0c;在每年規(guī)定的時(shí)間內(nèi),盡快拍出 100 張某榮的照片,這樣就可以添加到“某榮共享文件夾”了,我們的資源就擴(kuò)充了。
但是,好景不長(zhǎng)。某本聰發(fā)現(xiàn)大量的拍照,沖擊前 100,那很快就能達(dá)成了,拍照就沒(méi)有難度了。
而且照片質(zhì)量還差,好像誰(shuí)都能輕易的添加照片資源,這樣就保證不了某榮的照片質(zhì)量了。于是,某本聰再次降臨,增加拍照的共識(shí)難度。?
小 One 和小露作為忠實(shí)的粉絲,怎能放棄,他們買了高端相機(jī),讓某榮擺出各種姿勢(shì),花費(fèi)大量的時(shí)間和汗水完成了高質(zhì)量的照片,當(dāng)然自己也非常的辛苦。
這樣高質(zhì)量的照片就可以添加到“某榮的文件夾”中了。
2. 何為 ICO?
小 One 想:每張照片都是不可造假破壞的,所以具有唯一性,還有單獨(dú)編號(hào),給每一張照片估價(jià),它不就值錢了嗎?就像現(xiàn)實(shí)世界中無(wú)法復(fù)制的名畫一樣!
小 One 就把之前的某榮照片,構(gòu)造出來(lái)相應(yīng)的“某榮幣”,當(dāng)然這個(gè)行為就向我們的政府通過(guò)國(guó)庫(kù)的黃金數(shù)額發(fā)行等價(jià)的人民幣類似啦。
估值呢,當(dāng)然小 One 說(shuō)的算啦,人家是照片的擁有者嘛。
為了證實(shí)某榮幣 5W 塊一個(gè),小 One 首先購(gòu)買了 2100 個(gè)中的 1100 個(gè),剩下的發(fā)行給吃瓜群眾們啦,我們已經(jīng)用 5W 買一個(gè),說(shuō)明他已經(jīng)值這個(gè)價(jià)錢啦,剩下 1000 個(gè)大家一起買吧。
這樣的話如果 2100 個(gè)全部認(rèn)購(gòu)出去,2100 個(gè)某榮幣可以就估值 1.05 億塊哦~這種通過(guò)數(shù)字貨幣的發(fā)行而得到融資的過(guò)程就是 ICO 啦。
根據(jù)這個(gè)圖的含義,如果小 One 和小露是一個(gè)可信任的機(jī)構(gòu)或者公眾人物,還是可以相信他們的,當(dāng)然啦也會(huì)有很多不法分子惡意發(fā)行貨幣來(lái)套現(xiàn)的。
這也是我們國(guó)家為什么禁止 ICO 發(fā)行的原因啦,因?yàn)槟壳皼](méi)有一個(gè)完善的 ICO 監(jiān)管條例能夠保證發(fā)行貨幣的機(jī)構(gòu)的可信任性和合法的監(jiān)管他們,所以吃瓜群眾們要自己承擔(dān)風(fēng)險(xiǎn)找到一個(gè)可信的機(jī)構(gòu)。
這樣 2100 個(gè)某榮幣全部認(rèn)購(gòu)成功,基金成立,這就是 ICO。當(dāng)然吃瓜群眾也可以繼續(xù)拍照創(chuàng)造某榮幣,就是有點(diǎn)難罷了。
現(xiàn)在各位了解什么是區(qū)塊鏈和 ICO 了吧~下面手把手教你如何用 Python?語(yǔ)言創(chuàng)建一個(gè)區(qū)塊鏈?
03 用 Python 從 0 開(kāi)始創(chuàng)建一個(gè)區(qū)塊鏈
對(duì)數(shù)字貨幣的崛起感到新奇的我們,并且想知道其背后的技術(shù)——區(qū)塊鏈?zhǔn)窃鯓訉?shí)現(xiàn)的。本文通過(guò) Python 構(gòu)建一個(gè)區(qū)塊鏈可以加深對(duì)區(qū)塊鏈的理解。
1. 準(zhǔn)備工作
本文要求讀者對(duì) Python 有基本的理解,能讀寫基本的 Python,并且需要對(duì) HTTP 請(qǐng)求有基本的了解。
我們知道區(qū)塊鏈?zhǔn)怯蓞^(qū)塊的記錄構(gòu)成的不可變、有序的鏈結(jié)構(gòu),記錄可以是交易、文件或任何你想要的數(shù)據(jù),重要的是它們是通過(guò)哈希值(hashes)鏈接起來(lái)的。
如果你還不是很了解哈希,可以查看這篇文章https://learncryptography.com/hash-functions/what-are-hash-functions。
(1)環(huán)境準(zhǔn)備:
環(huán)境準(zhǔn)備,確保已經(jīng)安裝 Python3.6+、pip、Flask、requests。
(2)安裝方法:
pip install Flask==0.12.2 requests==2.18.4
同時(shí)還需要一個(gè) HTTP 客戶端,比如 Postman、cURL 或其他客戶端。
參考源代碼(原代碼在我翻譯的時(shí)候,無(wú)法運(yùn)行,我 fork 了一份,修復(fù)了其中的錯(cuò)誤,并添加了翻譯,感謝 star)。
2. 開(kāi)始創(chuàng)建 Blockchain
新建一個(gè)文件 blockchain.py,本文所有的代碼都寫在這一個(gè)文件中,可以隨時(shí)參考源代碼。
(1)Blockchain 類
首先創(chuàng)建一個(gè) Blockchain 類,在構(gòu)造函數(shù)中創(chuàng)建了兩個(gè)列表,一個(gè)用于儲(chǔ)存區(qū)塊鏈,一個(gè)用于儲(chǔ)存交易。
以下是 Blockchain 類的框架:
class Blockchain(object):
? ?def __init__(self):
? ? ? ?self.chain = []
? ? ? ?self.current_transactions = []
? ?def new_block(self):
? ? ? ?# Creates a new Block and adds it to the chain
? ? ? ?pass
? ?def new_transaction(self):
? ? ? ?# Adds a new transaction to the list of transactions
? ? ? ?pass
? ?@staticmethod
? ?def hash(block):
? ? ? ?# Hashes a Block
? ? ? ?pass
? ?@property
? ?def last_block(self):
? ? ? ?# Returns the last Block in the chain
? ? ? ?pass
Blockchain 類用來(lái)管理鏈條,它能存儲(chǔ)交易、加入新塊等,下面我們來(lái)進(jìn)一步完善這些方法。
(2)塊結(jié)構(gòu)
每個(gè)區(qū)塊包含屬性:索引(index)、Unix 時(shí)間戳(timestamp)、交易列表(transactions)、工作量證明(稍后解釋)以及前一個(gè)區(qū)塊的 Hash 值。
以下是一個(gè)區(qū)塊的結(jié)構(gòu):
block = {
? ?'index': 1,
? ?'timestamp': 1506057125.900785,
? ?'transactions': [
? ? ? ?{
? ? ? ? ? ?'sender': "8527147fe1f5426f9dd545de4b27ee00",
? ? ? ? ? ?'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",
? ? ? ? ? ?'amount': 5,
? ? ? ?}
? ?],
? ?'proof': 324984774000,
? ?'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
}
到這里區(qū)塊鏈的概念就清楚了,每個(gè)新的區(qū)塊都包含上一個(gè)區(qū)塊的 Hash,這是關(guān)鍵的一點(diǎn),它保障了區(qū)塊鏈的不可變性。
如果攻擊者破壞了前面的某個(gè)區(qū)塊,那么后面所有區(qū)塊的Hash都會(huì)變得不正確。不理解的話,慢慢消化,可參考{% post_link whatbc 區(qū)塊鏈技術(shù)原理 %}。
(3)加入交易
接下來(lái)我們需要添加一個(gè)交易,來(lái)完善下 new_transaction 方法:
class Blockchain(object):
? ?...
? ?def new_transaction(self, sender, recipient, amount):
? ? ? ?"""
? ? ? ?生成新交易信息,信息將加入到下一個(gè)待挖的區(qū)塊中
? ? ? ?:param sender: <str> Address of the Sender
? ? ? ?:param recipient: <str> Address of the Recipient
? ? ? ?:param amount: <int> Amount
? ? ? ?:return: <int> The index of the Block that will hold this transaction
? ? ? ?"""
? ? ? ?self.current_transactions.append({
? ? ? ? ? ?'sender': sender,
? ? ? ? ? ?'recipient': recipient,
? ? ? ? ? ?'amount': amount,
? ? ? ?})
? ? ? ?return self.last_block['index'] + 1
方法向列表中添加一個(gè)交易記錄,并返回該記錄將被添加到的區(qū)塊(下一個(gè)待挖掘的區(qū)塊)的索引,等下在用戶提交交易時(shí)會(huì)有用。
(4)創(chuàng)建新塊
當(dāng) Blockchain 實(shí)例化后,我們需要構(gòu)造一個(gè)創(chuàng)世塊(沒(méi)有前區(qū)塊的第一個(gè)區(qū)塊),并且給它加上一個(gè)工作量證明。每個(gè)區(qū)塊都需要經(jīng)過(guò)工作量證明,俗稱挖礦,稍后會(huì)繼續(xù)講解。
為了構(gòu)造創(chuàng)世塊,我們還需要完善 new_block(),new_transaction() 和hash() 方法:
import hashlib
import json
from time import time
class Blockchain(object):
? ?def __init__(self):
? ? ? ?self.current_transactions = []
? ? ? ?self.chain = []
? ? ? ?# Create the genesis block
? ? ? ?self.new_block(previous_hash=1, proof=100)
? ?def new_block(self, proof, previous_hash=None):
? ? ? ?"""
? ? ? ?生成新塊
? ? ? ?:param proof: <int> The proof given by the Proof of Work algorithm
? ? ? ?:param previous_hash: (Optional) <str> Hash of previous Block
? ? ? ?:return: <dict> New Block
? ? ? ?"""
? ? ? ?block = {
? ? ? ? ? ?'index': len(self.chain) + 1,
? ? ? ? ? ?'timestamp': time(),
? ? ? ? ? ?'transactions': self.current_transactions,
? ? ? ? ? ?'proof': proof,
? ? ? ? ? ?'previous_hash': previous_hash or self.hash(self.chain[-1]),
? ? ? ?}
? ? ? ?# Reset the current list of transactions
? ? ? ?self.current_transactions = []
? ? ? ?self.chain.append(block)
? ? ? ?return block
? ?def new_transaction(self, sender, recipient, amount):
? ? ? ?"""
? ? ? ?生成新交易信息,信息將加入到下一個(gè)待挖的區(qū)塊中
? ? ? ?:param sender: <str> Address of the Sender
? ? ? ?:param recipient: <str> Address of the Recipient
? ? ? ?:param amount: <int> Amount
? ? ? ?:return: <int> The index of the Block that will hold this transaction
? ? ? ?"""
? ? ? ?self.current_transactions.append({
? ? ? ? ? ?'sender': sender,
? ? ? ? ? ?'recipient': recipient,
? ? ? ? ? ?'amount': amount,
? ? ? ?})
? ? ? ?return self.last_block['index'] + 1
? ?@property
? ?def last_block(self):
? ? ? ?return self.chain[-1]
? ?@staticmethod
? ?def hash(block):
? ? ? ?"""
? ? ? ?生成塊的 SHA-256 hash值
? ? ? ?:param block: <dict> Block
? ? ? ?:return: <str>
? ? ? ?"""
? ? ? ?# We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes
? ? ? ?block_string = json.dumps(block, sort_keys=True).encode()
? ? ? ?return hashlib.sha256(block_string).hexdigest()
通過(guò)上面的代碼和注釋可以對(duì)區(qū)塊鏈有直觀的了解,接下來(lái)我們看看區(qū)塊是怎么挖出來(lái)的。
(5)理解工作量證明
新的區(qū)塊依賴工作量證明算法(PoW)來(lái)構(gòu)造。PoW 的目標(biāo)是找出一個(gè)符合特定條件的數(shù)字,這個(gè)數(shù)字很難計(jì)算出來(lái),但容易驗(yàn)證。這就是工作量證明的核心思想。
為了方便理解,舉個(gè)例子:
假設(shè)一個(gè)整數(shù) x 乘以另一個(gè)整數(shù) y 的積的 Hash 值必須以 0 結(jié)尾,即 hash(x * y) = ac23dc...0。設(shè)變量 x = 5,求 y 的值?
用 Python 實(shí)現(xiàn)如下:
from hashlib import sha256
x = 5
y = 0 ?# y未知
while sha256(f'{x*y}'.encode()).hexdigest()[-1] != "0":
? ?y += 1
print(f'The solution is y = {y}')
結(jié)果是:y = 21,因?yàn)?#xff1a;
hash(5 * 21) = 1253e9373e...5e3600155e860
在比特幣中,使用稱為 Hashcash 的工作量證明算法,它和上面的問(wèn)題很類似,礦工們?yōu)榱藸?zhēng)奪創(chuàng)建區(qū)塊的權(quán)利而爭(zhēng)相計(jì)算結(jié)果。
通常,計(jì)算難度與目標(biāo)字符串需要滿足的特定字符的數(shù)量成正比,礦工算出結(jié)果后,會(huì)獲得比特幣獎(jiǎng)勵(lì)。當(dāng)然,在網(wǎng)絡(luò)上非常容易驗(yàn)證這個(gè)結(jié)果。
(6)實(shí)現(xiàn)工作量證明
讓我們來(lái)實(shí)現(xiàn)一個(gè)相似 PoW 算法,規(guī)則是:尋找一個(gè)數(shù) p,使得它與前一個(gè)區(qū)塊的 proof 拼接成的字符串的 Hash 值以 4 個(gè)零開(kāi)頭。
import hashlib
import json
from time import time
from uuid import uuid4
class Blockchain(object):
? ?...
? ?def proof_of_work(self, last_proof):
? ? ? ?"""
? ? ? ?簡(jiǎn)單的工作量證明:
? ? ? ? - 查找一個(gè) p' 使得 hash(pp') 以4個(gè)0開(kāi)頭
? ? ? ? - p 是上一個(gè)塊的證明, ?p' 是當(dāng)前的證明
? ? ? ?:param last_proof: <int>
? ? ? ?:return: <int>
? ? ? ?"""
? ? ? ?proof = 0
? ? ? ?while self.valid_proof(last_proof, proof) is False:
? ? ? ? ? ?proof += 1
? ? ? ?return proof
? ?@staticmethod
? ?def valid_proof(last_proof, proof):
? ? ? ?"""
? ? ? ?驗(yàn)證證明: 是否hash(last_proof, proof)以4個(gè)0開(kāi)頭?
? ? ? ?:param last_proof: <int> Previous Proof
? ? ? ?:param proof: <int> Current Proof
? ? ? ?:return: <bool> True if correct, False if not.
? ? ? ?"""
? ? ? ?guess = f'{last_proof}{proof}'.encode()
? ? ? ?guess_hash = hashlib.sha256(guess).hexdigest()
? ? ? ?return guess_hash[:4] == "0000"
衡量算法復(fù)雜度的辦法是修改零開(kāi)頭的個(gè)數(shù)。使用 4 個(gè)零來(lái)用于演示,你會(huì)發(fā)現(xiàn)多一個(gè)零都會(huì)大大增加計(jì)算出結(jié)果所需的時(shí)間。
現(xiàn)在 Blockchain 類基本已經(jīng)完成了,接下來(lái)使用 HTTP requests 來(lái)進(jìn)行交互。
2. Blockchain 作為 API 接口
我們將使用 Python Flask 框架,這是一個(gè)輕量 Web 應(yīng)用框架,它方便將網(wǎng)絡(luò)請(qǐng)求映射到 Python 函數(shù),現(xiàn)在我們來(lái)讓 Blockchain 運(yùn)行在 Flask Web 上。
我們將創(chuàng)建三個(gè)接口:
/transactions/new 創(chuàng)建一個(gè)交易并添加到區(qū)塊
/mine 告訴服務(wù)器去挖掘新的區(qū)塊
/chain 返回整個(gè)區(qū)塊鏈
(1)創(chuàng)建節(jié)點(diǎn)
我們的“Flask 服務(wù)器”將扮演區(qū)塊鏈網(wǎng)絡(luò)中的一個(gè)節(jié)點(diǎn),我們先添加一些框架代碼:
import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4
from flask import Flask
class Blockchain(object):
? ?...
# Instantiate our Node
app = Flask(__name__)
# Generate a globally unique address for this node
node_identifier = str(uuid4()).replace('-', '')
# Instantiate the Blockchain
blockchain = Blockchain()
@app.route('/mine', methods=['GET'])
def mine():
? ?return "We'll mine a new Block"
@app.route('/transactions/new', methods=['POST'])
def new_transaction():
? ?return "We'll add a new transaction"
@app.route('/chain', methods=['GET'])
def full_chain():
? ?response = {
? ? ? ?'chain': blockchain.chain,
? ? ? ?'length': len(blockchain.chain),
? ?}
? ?return jsonify(response), 200
if __name__ == '__main__':
? ?app.run(host='0.0.0.0', port=5000)
簡(jiǎn)單的說(shuō)明一下以上代碼:
第 15 行:創(chuàng)建一個(gè)節(jié)點(diǎn)。
第 18 行:為節(jié)點(diǎn)創(chuàng)建一個(gè)隨機(jī)的名字。
第 21 行:實(shí)例 Blockchain 類。
第 24–26 行:創(chuàng)建 /mine GET 接口。
第 28–30 行:創(chuàng)建 /transactions/new POST 接口,可以給接口發(fā)送交易數(shù)據(jù)。
第 32–38 行:創(chuàng)建 /chain 接口, 返回整個(gè)區(qū)塊鏈。
第 40–41 行:服務(wù)運(yùn)行在端口 5000 上。
(2)發(fā)送交易
發(fā)送到節(jié)點(diǎn)的交易數(shù)據(jù)結(jié)構(gòu)如下:
{
"sender": "my address",
"recipient": "someone else's address",
"amount": 5
}
之前已經(jīng)有添加交易的方法,基于接口來(lái)添加交易就很簡(jiǎn)單了:
import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4
from flask import Flask, jsonify, request
...
@app.route('/transactions/new', methods=['POST'])
def new_transaction():
? ?values = request.get_json()
? ?# Check that the required fields are in the POST'ed data
? ?required = ['sender', 'recipient', 'amount']
? ?if not all(k in values for k in required):
? ? ? ?return 'Missing values', 400
? ?# Create a new Transaction
? ?index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])
? ?response = {'message': f'Transaction will be added to Block {index}'}
? ?return jsonify(response), 201
(3)挖礦
挖礦正是神奇所在,它很簡(jiǎn)單,做了以下三件事:
計(jì)算工作量證明 PoW。
通過(guò)新增一個(gè)交易授予礦工(自己)一個(gè)幣。
構(gòu)造新區(qū)塊并將其添加到鏈中。
import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4
from flask import Flask, jsonify, request
...
import hashlib
import json
from time import time
from uuid import uuid4
from flask import Flask, jsonify, request
...
@app.route('/mine', methods=['GET'])
def mine():
? ?# We run the proof of work algorithm to get the next proof...
? ?last_block = blockchain.last_block
? ?last_proof = last_block['proof']
? ?proof = blockchain.proof_of_work(last_proof)
? ?# 給工作量證明的節(jié)點(diǎn)提供獎(jiǎng)勵(lì).
? ?# 發(fā)送者為 "0" 表明是新挖出的幣
? ?blockchain.new_transaction(
? ? ? ?sender="0",
? ? ? ?recipient=node_identifier,
? ? ? ?amount=1,
? ?)
? ?# Forge the new Block by adding it to the chain
? ?block = blockchain.new_block(proof)
? ?response = {
? ? ? ?'message': "New Block Forged",
? ? ? ?'index': block['index'],
? ? ? ?'transactions': block['transactions'],
? ? ? ?'proof': block['proof'],
? ? ? ?'previous_hash': block['previous_hash'],
? ?}
? ?return jsonify(response), 200
注意交易的接收者是我們自己的服務(wù)器節(jié)點(diǎn),我們做的大部分工作都只是圍繞 Blockchain 類方法進(jìn)行交互。到此,我們的區(qū)塊鏈就算完成了,我們來(lái)實(shí)際運(yùn)行下。
(4)運(yùn)行區(qū)塊鏈
你可以使用 cURL 或 Postman 去和 API 進(jìn)行交互。
啟動(dòng) server:
$ python blockchain.py
* Runing on http://127.0.0.1:5000/ (Press CTRL+C to quit)
讓我們通過(guò)請(qǐng)求 http://localhost:5000/mine 來(lái)進(jìn)行挖礦:
通過(guò) post 請(qǐng)求,添加一個(gè)新交易:
如果不是使用 Postman,則用以下的 cURL 語(yǔ)句也是一樣的:
$ curl -X POST -H "Content-Type: application/json" -d '{
"sender": "d4ee26eee15148ee92c6cd394edd974e",
"recipient": "someone-other-address",
"amount": 5
}' "http://localhost:5000/transactions/new"
在挖了兩次礦之后,就有 3 個(gè)塊了,通過(guò)請(qǐng)求 http://localhost:5000/chain 可以得到所有的塊信息。
{
?"chain": [
? ?{
? ? ?"index": 1,
? ? ?"previous_hash": 1,
? ? ?"proof": 100,
? ? ?"timestamp": 1506280650.770839,
? ? ?"transactions": []
? ?},
? ?{
? ? ?"index": 2,
? ? ?"previous_hash": "c099bc...bfb7",
? ? ?"proof": 35293,
? ? ?"timestamp": 1506280664.717925,
? ? ?"transactions": [
? ? ? ?{
? ? ? ? ?"amount": 1,
? ? ? ? ?"recipient": "8bbcb347e0634905b0cac7955bae152b",
? ? ? ? ?"sender": "0"
? ? ? ?}
? ? ?]
? ?},
? ?{
? ? ?"index": 3,
? ? ?"previous_hash": "eff91a...10f2",
? ? ?"proof": 35089,
? ? ?"timestamp": 1506280666.1086972,
? ? ?"transactions": [
? ? ? ?{
? ? ? ? ?"amount": 1,
? ? ? ? ?"recipient": "8bbcb347e0634905b0cac7955bae152b",
? ? ? ? ?"sender": "0"
? ? ? ?}
? ? ?]
? ?}
?],
?"length": 3
}
3. 一致性(共識(shí))
我們已經(jīng)有了一個(gè)基本的區(qū)塊鏈可以接受交易和挖礦,但是區(qū)塊鏈系統(tǒng)應(yīng)該是分布式的。
既然是分布式的,那么我們究竟拿什么保證所有節(jié)點(diǎn)有同樣的鏈呢?這就是一致性問(wèn)題,我們要想在網(wǎng)絡(luò)上有多個(gè)節(jié)點(diǎn),就必須實(shí)現(xiàn)一個(gè)一致性的算法。
(1)注冊(cè)節(jié)點(diǎn)
在實(shí)現(xiàn)一致性算法之前,我們需要找到一種方式讓一個(gè)節(jié)點(diǎn)知道它相鄰的節(jié)點(diǎn)。
每個(gè)節(jié)點(diǎn)都需要保存一份包含網(wǎng)絡(luò)中其他節(jié)點(diǎn)的記錄,因此讓我們新增幾個(gè)接口:
/nodes/register 接收 URL 形式的新節(jié)點(diǎn)列表。
/nodes/resolve 執(zhí)行一致性算法,解決任何沖突,確保節(jié)點(diǎn)擁有正確的鏈。
我們修改下 Blockchain 的 init 函數(shù)并提供一個(gè)注冊(cè)節(jié)點(diǎn)方法:
...
from urllib.parse import urlparse
...
class Blockchain(object):
? ?def __init__(self):
? ? ? ?...
? ? ? ?self.nodes = set()
? ? ? ?...
? ?def register_node(self, address):
? ? ? ?"""
? ? ? ?Add a new node to the list of nodes
? ? ? ?:param address: <str> Address of node. Eg. 'http://192.168.0.5:5000'
? ? ? ?:return: None
? ? ? ?"""
? ? ? ?parsed_url = urlparse(address)
? ? ? ?self.nodes.add(parsed_url.netloc)
我們用 set 來(lái)儲(chǔ)存節(jié)點(diǎn),這是一種避免重復(fù)添加節(jié)點(diǎn)的簡(jiǎn)單方法。
(2)實(shí)現(xiàn)共識(shí)算法
前面提到,沖突是指不同的節(jié)點(diǎn)擁有不同的鏈,為了解決這個(gè)問(wèn)題,規(guī)定最長(zhǎng)的、有效的鏈才是最終的鏈,換句話說(shuō),網(wǎng)絡(luò)中有效最長(zhǎng)鏈才是實(shí)際的鏈。
我們使用以下的算法,來(lái)達(dá)到網(wǎng)絡(luò)中的共識(shí):
...
import requests
class Blockchain(object)
? ?...
? ?def valid_chain(self, chain):
? ? ? ?"""
? ? ? ?Determine if a given blockchain is valid
? ? ? ?:param chain: <list> A blockchain
? ? ? ?:return: <bool> True if valid, False if not
? ? ? ?"""
? ? ? ?last_block = chain[0]
? ? ? ?current_index = 1
? ? ? ?while current_index < len(chain):
? ? ? ? ? ?block = chain[current_index]
? ? ? ? ? ?print(f'{last_block}')
? ? ? ? ? ?print(f'{block}')
? ? ? ? ? ?print("\n-----------\n")
? ? ? ? ? ?# Check that the hash of the block is correct
? ? ? ? ? ?if block['previous_hash'] != self.hash(last_block):
? ? ? ? ? ? ? ?return False
? ? ? ? ? ?# Check that the Proof of Work is correct
? ? ? ? ? ?if not self.valid_proof(last_block['proof'], block['proof']):
? ? ? ? ? ? ? ?return False
? ? ? ? ? ?last_block = block
? ? ? ? ? ?current_index += 1
? ? ? ?return True
? ?def resolve_conflicts(self):
? ? ? ?"""
? ? ? ?共識(shí)算法解決沖突
? ? ? ?使用網(wǎng)絡(luò)中最長(zhǎng)的鏈.
? ? ? ?:return: <bool> True 如果鏈被取代, 否則為False
? ? ? ?"""
? ? ? ?neighbours = self.nodes
? ? ? ?new_chain = None
? ? ? ?# We're only looking for chains longer than ours
? ? ? ?max_length = len(self.chain)
? ? ? ?# Grab and verify the chains from all the nodes in our network
? ? ? ?for node in neighbours:
? ? ? ? ? ?response = requests.get(f'http://{node}/chain')
? ? ? ? ? ?if response.status_code == 200:
? ? ? ? ? ? ? ?length = response.json()['length']
? ? ? ? ? ? ? ?chain = response.json()['chain']
? ? ? ? ? ? ? ?# Check if the length is longer and the chain is valid
? ? ? ? ? ? ? ?if length > max_length and self.valid_chain(chain):
? ? ? ? ? ? ? ? ? ?max_length = length
? ? ? ? ? ? ? ? ? ?new_chain = chain
? ? ? ?# Replace our chain if we discovered a new, valid chain longer than ours
? ? ? ?if new_chain:
? ? ? ? ? ?self.chain = new_chain
? ? ? ? ? ?return True
? ? ? ?return False
第一個(gè)方法 valid_chain() 用來(lái)檢查是否是有效鏈,遍歷每個(gè)塊驗(yàn)證 hash 和 proof。
第二個(gè)方法 resolve_conflicts() 用來(lái)解決沖突,遍歷所有的鄰居節(jié)點(diǎn),并用上一個(gè)方法檢查鏈的有效性, 如果發(fā)現(xiàn)有效更長(zhǎng)鏈,就替換掉自己的鏈。
讓我們添加兩個(gè)路由,一個(gè)用來(lái)注冊(cè)節(jié)點(diǎn),一個(gè)用來(lái)解決沖突。
@app.route('/nodes/register', methods=['POST'])
def register_nodes():
? ?values = request.get_json()
? ?nodes = values.get('nodes')
? ?if nodes is None:
? ? ? ?return "Error: Please supply a valid list of nodes", 400
? ?for node in nodes:
? ? ? ?blockchain.register_node(node)
? ?response = {
? ? ? ?'message': 'New nodes have been added',
? ? ? ?'total_nodes': list(blockchain.nodes),
? ?}
? ?return jsonify(response), 201
@app.route('/nodes/resolve', methods=['GET'])
def consensus():
? ?replaced = blockchain.resolve_conflicts()
? ?if replaced:
? ? ? ?response = {
? ? ? ? ? ?'message': 'Our chain was replaced',
? ? ? ? ? ?'new_chain': blockchain.chain
? ? ? ?}
? ?else:
? ? ? ?response = {
? ? ? ? ? ?'message': 'Our chain is authoritative',
? ? ? ? ? ?'chain': blockchain.chain
? ? ? ?}
? ?return jsonify(response), 200
你可以在不同的機(jī)器運(yùn)行節(jié)點(diǎn),或在一臺(tái)機(jī)機(jī)開(kāi)啟不同的網(wǎng)絡(luò)端口來(lái)模擬多節(jié)點(diǎn)的網(wǎng)絡(luò)。
這里在同一臺(tái)機(jī)器開(kāi)啟不同的端口演示,在不同的終端運(yùn)行以下命令,就啟動(dòng)了兩個(gè)節(jié)點(diǎn):
http://localhost:5000?
http://localhost:5001
pipenv run python blockchain.py
pipenv run python blockchain.py -p 5001
然后在節(jié)點(diǎn) 2 上挖兩個(gè)塊,確保是更長(zhǎng)的鏈,然后在節(jié)點(diǎn) 1 上訪問(wèn)接口 /nodes/resolve,這時(shí)節(jié)點(diǎn) 1 的鏈會(huì)通過(guò)共識(shí)算法被節(jié)點(diǎn) 2 的鏈取代。
好啦,你可以邀請(qǐng)朋友們一起來(lái)測(cè)試你的區(qū)塊鏈。
來(lái)源:51CTO技術(shù)棧(ID:blog51cto)?綜合整理
文章版權(quán)歸原作者所有,轉(zhuǎn)載僅供學(xué)習(xí)使用,不用于任何商業(yè)用途,如有侵權(quán)請(qǐng)留言聯(lián)系刪除,感謝合作。
總結(jié)
以上是生活随笔為你收集整理的看完此文再不懂区块链算我输:手把手教你用Python从零开始创建区块链的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 为什么软件工程师找不到工作?我想分享四个
- 下一篇: 干货整理:处理不平衡数据的技巧总结!收好