學(xué)習(xí)區(qū)塊鏈的最好方法是構(gòu)建一個(上)
深入理解區(qū)塊鏈最好的方式莫過于親手搭建一個,在這個過程中理解它背后的邏輯和原理。
你來這里是因為和我一樣,你對加密貨幣的崛起感到興奮。你想了解區(qū)塊鏈?zhǔn)侨绾喂ぷ鞯?,它們背后的基本技術(shù)原理是怎樣的。
但了解區(qū)塊鏈并不容易,至少對我來說不是很容易的。我喜歡邊干邊學(xué)。它迫使我在代碼級別上處理問題,這種方法可以讓我堅持學(xué)習(xí)下去。
記住,區(qū)塊鏈?zhǔn)且粋€不可變的、有順序的鏈記錄,我們稱之為區(qū)塊。它們可以包含交易、文件或任何你想要的數(shù)據(jù)。但重要的是它們是用哈希鏈接在一起。
這個指南最適合的閱讀對象的要求是什么?至少你輕松地閱讀和編寫一些基本的Python,并了解HTTP請求是如何工作的,因為我們將通過HTTP協(xié)議與我們的 Blockchain 進(jìn)行交互。
需要什么工具和環(huán)境?確保安裝了Python 3.6+(以及 pip ),還需要安裝Flask和Requests庫:
pip install Flask==0.12.2 requests==2.18.4
你還需要一個HTTP客戶端,比如Postman或cURL??捎玫脑创a請點擊:https://github.com/dvf/blockchain
第一步:構(gòu)建Blockchain
打開你喜歡的文本編輯器或IDE,我比較喜歡使用 PyCharm。然后創(chuàng)建一個名為blockchain.py的新文件。只使用這一個文件,但是如果搞丟了此文件,你可以一直引用源代碼:https://github.com/dvf/blockchain
(1)區(qū)塊鏈藍(lán)圖
我們將創(chuàng)建一個區(qū)塊鏈 類,它的構(gòu)造函數(shù)會創(chuàng)建一個初始空列表用于存儲區(qū)塊鏈,另一個用于存儲交易。這是我們創(chuàng)建的區(qū)塊鏈class?的源碼:
1.?????????????class?Blockchain(object):
2.?????????????????def?__init__(self):
3.?????????????????????self.chain?=?[]
4.?????????????????????self.current_transactions?=?[]
5.
6.?????????????????def?new_block(self):
7.?????????????????????#?Creates?a?new?Block?and?adds?it?to?the?chain
8.?????????????????????pass
9.
10.??????????????def?new_transaction(self):
11.???????????????????#?Adds?a?new?transaction?to?the?list?of?transactions
12.??????????????????pass
13.
14.??????????????@staticmethod
15.??????????????def?hash(block):
16.??????????????????#?Hashes?a?Block
17.??????????????????pass
18.
19.??????????????@property
20.??????????????def?last_block(self):
21.??????????????????#?Returns?the?last?Block?in?the?chain
22.??????????????????pass
Blueprint of our Blockchain Class
區(qū)塊鏈 class 負(fù)責(zé)管理鏈。它將存儲交易,并有一些輔助方法來為鏈添加新的區(qū)塊。讓我們開始充實一些方法。
一個區(qū)塊會是什么樣子?
每個塊都有一個索引、一個時間戳(Unix時間)、一個交易列表、一個證明和前一個塊的哈希值。
區(qū)塊源碼例子:
1.?????????????block?=?{
2.?????????????????‘index’:?1,
3.?????????????????‘timestamp’:?1506057125.900785,
4.?????????????????‘transactions’:?[
5.?????????????????????{
6.?????????????????????????‘sender’:?“8527147fe1f5426f9dd545de4b27ee00”,
7.?????????????????????????‘recipient’:?“a77f5cdfa2934df3954a5c7c7da5df1f”,
8.?????????????????????????‘amount’:?5,
9.?????????????????????}
10.??????????????],
11.???????????????‘proof’:?324984774000,
12.??????????????‘previous_hash’:?“2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824”
13.??????????}
鏈的概念應(yīng)該很明顯:每個新塊都包含在其內(nèi)部的前一個塊的哈希。這點是至關(guān)重要的,因為它使 Blockchain 不可篡改:如果攻擊者破壞了鏈中較早的區(qū)塊,那么隨后所有的塊都將包含不正確的哈希值。
請花一些時間好好去理解它——這是區(qū)塊鏈設(shè)計的的核心理念。
(2)在區(qū)塊中添加交易
我們需要一種將交易添加到塊中的方法。new_transaction() 方法可以實現(xiàn)這個功能,而且非常簡單:
1.?????????????class?Blockchain(object):
2.?????????????????…
3.
4.?????????????????def?new_transaction(self,?sender,?recipient,?amount):
5.?????????????????????“””
6.?????????????????????Creates?a?new?transaction?to?go?into?the?next?mined?Block
7.
8.?????????????????????:param?sender:?<str>?Address?of?the?Sender
9.?????????????????????:param?recipient:?<str>?Address?of?the?Recipient
10.??????????????????:param?amount:?<int>?Amount
11.???????????????????:return:?<int>?The?index?of?the?Block?that?will?hold?this?transaction
12.??????????????????“””
13.
14.??????????????????self.current_transactions.append({
15.??????????????????????‘sender’:?sender,
16.??????????????????????‘recipient’:?recipient,
17.??????????????????????‘amount’:?amount,
18.??????????????????})
19.
20.??????????????????return?self.last_block[‘index’]?+?1
在new_transaction()將交易添加到列表之后,它將返回這個交易會被添加到下一個塊的索引。這對稍后提交交易的用戶有用。
(3)創(chuàng)建新區(qū)塊
當(dāng) 區(qū)塊鏈被實例化時,需要將它與一個沒有前輩的創(chuàng)世區(qū)塊一起連接起來。我們還需要向我們的創(chuàng)世區(qū)塊添加一個“證明”,這是挖礦的結(jié)果。
除了在我們的構(gòu)造函數(shù)中創(chuàng)建創(chuàng)世區(qū)塊之外,我們還將為new_block()、new_transaction()和hash()添加方法:
1.?????????????import?hashlib
2.?????????????import?json
3.?????????????from?time?import?time
4.
5.
6.?????????????class?Blockchain(object):
7.?????????????????def?__init__(self):
8.?????????????????????self.current_transactions?=?[]
9.?????????????????????self.chain?=?[]
10.
11.???????????????????#?Create?the?genesis?block
12.??????????????????self.new_block(previous_hash=1,?proof=100)
13.
14.??????????????def?new_block(self,?proof,?previous_hash=None):
15.??????????????????“””
16.??????????????????Create?a?new?Block?in?the?Blockchain
17.
18.??????????????????:param?proof:?<int>?The?proof?given?by?the?Proof?of?Work?algorithm
19.??????????????????:param?previous_hash:?(Optional)?<str>?Hash?of?previous?Block
20.??????????????????:return:?<dict>?New?Block
21.??????????????????“””
22.
23.??????????????????block?=?{
24.??????????????????????‘index’:?len(self.chain)?+?1,
25.??????????????????????‘timestamp’:?time(),
26.??????????????????????‘transactions’:?self.current_transactions,
27.??????????????????????‘proof’:?proof,
28.??????????????????????‘previous_hash’:?previous_hash?or?self.hash(self.chain[-1]),
29.??????????????????}
30.
31.??????????????????#?Reset?the?current?list?of?transactions
32.??????????????????self.current_transactions?=?[]
33.
34.??????????????????self.chain.append(block)
35.??????????????????return?block
36.
37.??????????????def?new_transaction(self,?sender,?recipient,?amount):
38.??????????????????“””
39.??????????????????Creates?a?new?transaction?to?go?into?the?next?mined?Block
40.
41.??????????????????:param?sender:?<str>?Address?of?the?Sender
42.??????????????????:param?recipient:?<str>?Address?of?the?Recipient
43.??????????????????:param?amount:?<int>?Amount
44.??????????????????:return:?<int>?The?index?of?the?Block?that?will?hold?this?transaction
45.??????????????????“””
46.??????????????????self.current_transactions.append({
47.??????????????????????‘sender’:?sender,
48.??????????????????????‘recipient’:?recipient,
49.??????????????????????‘amount’:?amount,
50.??????????????????})
51.
52.??????????????????return?self.last_block[‘index’]?+?1
53.
54.??????????????@property
55.??????????????def?last_block(self):
56.??????????????????return?self.chain[-1]
57.
58.??????????????@staticmethod
59.??????????????def?hash(block):
60.??????????????????“””
61.??????????????????Creates?a?SHA-256?hash?of?a?Block
62.
63.??????????????????:param?block:?<dict>?Block
64.??????????????????:return:?<str>
65.??????????????????“””
66.
67.??????????????????#?We?must?make?sure?that?the?Dictionary?is?Ordered,?or?we’ll?have?inconsistent?hashes
68.??????????????????block_string?=?json.dumps(block,?sort_keys=True).encode()
69.??????????????????return?hashlib.sha256(block_string).hexdigest()
70.
至此,我們幾乎完成了 Blockchain 的代碼化表現(xiàn)。但新的區(qū)塊是如何被創(chuàng)建、挖掘的?
(4)理解PoW工作量證明
工作量證明,也就是新的區(qū)塊如何在 Blockchain 上被創(chuàng)建或挖掘出來。它的目標(biāo)是發(fā)現(xiàn)一個解決問題的數(shù)字,這個數(shù)字一定很難找到,但卻很容易被驗證——在網(wǎng)絡(luò)上的任何人都可以通過計算來驗證,這是工作證明PoW背后的核心思想。
我們來看一個非常簡單的例子:我們想找到這樣一個數(shù)值,將整數(shù)x與另一個數(shù)值y的乘積進(jìn)行hash運(yùn)算,使得運(yùn)算的結(jié)果是一串字符串的結(jié)尾必須是數(shù)字0 。用數(shù)學(xué)表達(dá)式表示出來就是:
hash(x * y) = ac23dc…0
我們假定x = 5。在Python中實現(xiàn),代碼如下:
1.?????????????from?hashlib?import?sha256
2.?????????????x?=?5
3.?????????????y?=?0??#?We?don’t?know?what?y?should?be?yet…
4.?????????????while?sha256(f'{x*y}’.encode()).hexdigest()[-1]?!=?“0”:
5.?????????????????y?+=?1
6.?????????????print(f’The?solution?is?y?=?{y}’)
這里的解是y = 21。因為,生成的hash值是以0結(jié)尾的:
1.?hash(5?*?21)?=?1253e9373e…5e3600155e860
在比特幣中,工作量證明被稱為Hashcash 。它和上面舉出的簡單例子基本沒有太大區(qū)別。這是為了創(chuàng)建一個新的區(qū)塊,礦工們競相解決問題的算法。一般來說,難度取決于字符串中搜索的字符數(shù)。
礦工會因為在一個交易中找到了那個難題的解,而獲得系統(tǒng)給出的激勵:該網(wǎng)絡(luò)的一定量的數(shù)字貨幣。該網(wǎng)絡(luò)能夠很容易地驗證他們的解是否正確。
(5)實現(xiàn)基本的工作量證明
為區(qū)塊鏈實現(xiàn)一個類似的算法,規(guī)則與上面類似:找到一個數(shù)字p,當(dāng)與上一個區(qū)塊的解進(jìn)行哈希運(yùn)算時,產(chǎn)生一個前4位都是0的哈希值。
為了調(diào)整算法的難度,我們可以修改前幾位零的個數(shù)。但4個0就足夠了。你將發(fā)現(xiàn),添加一個前導(dǎo)零就會對找到解所需的時間造成很大的不同。
1.?????????????import?hashlib
2.?????????????import?json
3.
4.?????????????from?time?import?time
5.?????????????from?uuid?import?uuid4
6.
7.
8.?????????????class?Blockchain(object):
9.?????????????????…
10.
11.???????????????def?proof_of_work(self,?last_proof):
12.??????????????????“””
13.??????????????????Simple?Proof?of?Work?Algorithm:
14.???????????????????–?Find?a?number?p’?such?that?hash(pp’)?contains?leading?4?zeroes,?where?p?is?the?previous?p’
15.???????????????????–?p?is?the?previous?proof,?and?p’?is?the?new?proof
16.
17.??????????????????:param?last_proof:?<int>
18.??????????????????:return:?<int>
19.??????????????????“””
20.
21.??????????????????proof?=?0
22.??????????????????while?self.valid_proof(last_proof,?proof)?is?False:
23.??????????????????????proof?+=?1
24.
25.??????????????????return?proof
26.
27.??????????????@staticmethod
28.??????????????def?valid_proof(last_proof,?proof):
29.??????????????????“””
30.??????????????????Validates?the?Proof:?Does?hash(last_proof,?proof)?contain?4?leading?zeroes?
31.
32.??????????????????:param?last_proof:?<int>?Previous?Proof
33.??????????????????:param?proof:?<int>?Current?Proof
34.??????????????????:return:?<bool>?True?if?correct,?False?if?not.
35.??????????????????“””
36.
37.??????????????????guess?=?f'{last_proof}{proof}’.encode()
38.??????????????????guess_hash?=?hashlib.sha256(guess).hexdigest()
39.??????????????????return?guess_hash[:4]?==?“0000”
我們的類接近完成,我們已經(jīng)準(zhǔn)備好使用HTTP請求開始與它交互。
第二步:將區(qū)塊鏈作為API使用起來
使用Python的Flask框架。它是一個微型框架,它可以很容易地將端點映射到Python函數(shù)。這讓我們使用HTTP請求在web上與 Blockchain 進(jìn)行交互。
我們將創(chuàng)建三個方法:
- /transactions/new?? 創(chuàng)建一個新的交易到一個區(qū)塊。
- /mine?? 告訴我們的服務(wù)器去挖掘一個新的區(qū)塊。
- /chain?返回完整的 Blockchain 。
設(shè)置Flask
我們的“服務(wù)器”將在 Blockchain 網(wǎng)絡(luò)中形成單獨(dú)節(jié)點,創(chuàng)建一些樣板代碼如下所示:
1.?????????????import?hashlib
2.?????????????import?json
3.?????????????from?textwrap?import?dedent
4.?????????????from?time?import?time
5.?????????????from?uuid?import?uuid4
6.
7.?????????????from?flask?import?Flask
8.
9.
10.??????????class?Blockchain(object):
11.???????????????…
12.
13.
14.??????????#?Instantiate?our?Node
15.??????????app?=?Flask(__name__)
16.
17.??????????#?Generate?a?globally?unique?address?for?this?node
18.??????????node_identifier?=?str(uuid4()).replace(‘-‘,?”)
19.
20.??????????#?Instantiate?the?Blockchain
21.??????????blockchain?=?Blockchain()
22.
23.
24.??????????@app.route(‘/mine’,?methods=[‘GET’])
25.??????????def?mine():
26.??????????????return?“We’ll?mine?a?new?Block”
27.
28.??????????@app.route(‘/transactions/new’,?methods=[‘POST’])
29.??????????def?new_transaction():
30.??????????????return?“We’ll?add?a?new?transaction”
31.
32.??????????@app.route(‘/chain’,?methods=[‘GET’])
33.??????????def?full_chain():
34.??????????????response?=?{
35.??????????????????‘chain’:?blockchain.chain,
36.??????????????????‘length’:?len(blockchain.chain),
37.??????????????}
38.??????????????return?jsonify(response),?200
39.
40.??????????if?__name__?==?‘__main__’:
41.??????????????app.run(host=’0.0.0.0′,?port=5000)
關(guān)于在上面代碼中添加的內(nèi)容的簡要說明如下:
- Line 15:?實例化節(jié)點。
- Line 18:?為我們的節(jié)點創(chuàng)建一個隨機(jī)名稱。
- Line 21:?實例化我們的Blockchain類。
- Line 24–26:?創(chuàng)建/mine 端點,這是一個GET請求。
- Line 28–30:?創(chuàng)建 /transactions/new?端點,這是一個POST 請求,因為我們將向它發(fā)送數(shù)據(jù)。
- Line 32–38:?創(chuàng)建/chain端點,它返回完整的 Blockchain 。
- Line 40–41:?在端口5000上運(yùn)行服務(wù)器。
交易端點
這就是交易請求的樣子。這是用戶發(fā)送給服務(wù)器的內(nèi)容:
1.?????????????{
2.??????????????“sender”:?“my?address”,
3.??????????????“recipient”:?“someone?else’s?address”,
4.??????????????“amount”:?5
5.?????????????}
由于已經(jīng)有了將交易添加到區(qū)塊的類的方法,其余的都很簡單。讓我們編寫添加交易的函數(shù):
1.?????????????import?hashlib
2.?????????????import?json
3.?????????????from?textwrap?import?dedent
4.?????????????from?time?import?time
5.?????????????from?uuid?import?uuid4
6.
7.?????????????from?flask?import?Flask,?jsonify,?request
8.
9.?????????????…
10.
11.???????????@app.route(‘/transactions/new’,?methods=[‘POST’])
12.??????????def?new_transaction():
13.??????????????values?=?request.get_json()
14.
15.??????????????#?Check?that?the?required?fields?are?in?the?POST’ed?data
16.??????????????required?=?[‘sender’,?‘recipient’,?‘amount’]
17.??????????????if?not?all(k?in?values?for?k?in?required):
18.??????????????????return?‘Missing?values’,?400
19.
20.??????????????#?Create?a?new?Transaction
21.??????????????index?=?blockchain.new_transaction(values[‘sender’],?values[‘recipient’],?values[‘amount’])
22.
23.??????????????response?=?{‘message’:?f’Transaction?will?be?added?to?Block?{index}’}
24.??????????????return?jsonify(response),?201
Amethod for creating Transactions
挖礦端點
挖礦端點必須做三件事:
(1)計算工作量證明。
(2)通過增加一筆交易,獎賞給礦工(也就是我們自己)一定量的數(shù)字貨幣。
(3)通過將新區(qū)塊添加到鏈中來鍛造區(qū)塊。
1.?????????????import?hashlib
2.?????????????import?json
3.
4.?????????????from?time?import?time
5.?????????????from?uuid?import?uuid4
6.
7.?????????????from?flask?import?Flask,?jsonify,?request
8.
9.?????????????…
10.
11.???????????@app.route(‘/mine’,?methods=[‘GET’])
12.??????????def?mine():
13.??????????????#?We?run?the?proof?of?work?algorithm?to?get?the?next?proof…
14.??????????????last_block?=?blockchain.last_block
15.??????????????last_proof?=?last_block[‘proof’]
16.??????????????proof?=?blockchain.proof_of_work(last_proof)
17.
18.??????????????#?We?must?receive?a?reward?for?finding?the?proof.
19.??????????????#?The?sender?is?“0”?to?signify?that?this?node?has?mined?a?new?coin.
20.??????????????blockchain.new_transaction(
21.??????????????????sender=”0″,
22.??????????????????recipient=node_identifier,
23.??????????????????amount=1,
24.??????????????)
25.
26.??????????????#?Forge?the?new?Block?by?adding?it?to?the?chain
27.??????????????previous_hash?=?blockchain.hash(last_block)
28.??????????????block?=?blockchain.new_block(proof,?previous_hash)
29.
30.??????????????response?=?{
31.??????????????????‘message’:?“New?Block?Forged”,
32.??????????????????‘index’:?block[‘index’],
33.??????????????????‘transactions’:?block[‘transactions’],
34.??????????????????‘proof’:?block[‘proof’],
35.??????????????????‘previous_hash’:?block[‘previous_hash’],
36.??????????????}
37.??????????????return?jsonify(response),?200
被挖掘出來的區(qū)塊的接收者是我們節(jié)點的地址。在這里所做的大部分工作只是與Blockchain class中的方法進(jìn)行交互。在這一點上,我們已經(jīng)完成了,并且可以開始與我們的 Blockchain 進(jìn)行交互了。
——未完待續(xù)——
風(fēng)險警示:藍(lán)狐所有文章都不構(gòu)成投資推薦,投資有風(fēng)險,建議對項目進(jìn)行深入考察,慎重做好自己的投資決策。
原文作者:Danielvan Flymen
原文地址:hackernoon.com
譯者:由藍(lán)狐筆記社群“iGreenMind”翻譯
本文由 @藍(lán)狐筆記社群“iGreenMind” 翻譯發(fā)布于人人都是產(chǎn)品經(jīng)理。未經(jīng)許可,禁止轉(zhuǎn)載。
題圖來自 Pexels,基于 CC0 協(xié)議
- 目前還沒評論,等你發(fā)揮!