前言:深入理解區塊鏈最好的方式莫過於親手搭建一個,在這個過程中理解它背後的邏輯和原理。本文作者是Danielvan Flymen ,文章來源於hackernoon.com,由藍狐筆記社群“iGreenMind”翻譯。
你來這裡是因為和我一樣,你對加密貨幣的崛起感到興奮。你想了解區塊鏈是如何工作的,它們背後的基本技術原理是怎樣的。
但瞭解區塊鏈並不容易,至少對我來說不是很容易的。我喜歡邊幹邊學。它迫使我在代碼級別上處理問題,這種方法可以讓我堅持學習下去。
記住,區塊鏈是一個不可變的、有順序的鏈記錄,我們稱之為區塊。它們可以包含交易、文件或任何你想要的數據。但重要的是它們是用哈希鏈接在一起。
這個指南最適合的閱讀對象的要求是什麼?至少你輕鬆地閱讀和編寫一些基本的Python,並瞭解HTTP請求是如何工作的,因為我們將通過HTTP協議與我們的 Blockchain 進行交互。
需要什麼工具和環境?確保安裝了Python 3.6+(以及 pip ),還需要安裝Flask和Requests庫:
pip install Flask==0.12.2 requests==2.18.4
你還需要一個HTTP客戶端,比如Postman或cURL。可用的源代碼請點擊:https://github.com/dvf/blockchain
第一步:構建Blockchain
打開你喜歡的文本編輯器或IDE,我比較喜歡使用 PyCharm。然後創建一個名為blockchain.py的新文件。只使用這一個文件,但是如果搞丟了此文件,你可以一直引用源代碼:https://github.com/dvf/blockchain
區塊鏈藍圖
我們將創建一個區塊鏈 類,它的構造函數會創建一個初始空列表用於存儲區塊鏈,另一個用於存儲交易。這是我們創建的區塊鏈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
區塊鏈 class 負責管理鏈。它將存儲交易,並有一些輔助方法來為鏈添加新的區塊。讓我們開始充實一些方法。
一個區塊會是什麼樣子?
每個塊都有一個索引、一個時間戳(Unix時間)、一個交易列表、一個證明和前一個塊的哈希值。
區塊源碼例子:
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. }
鏈的概念應該很明顯:每個新塊都包含在其內部的前一個塊的哈希。這點是至關重要的,因為它使 Blockchain 不可篡改:如果攻擊者破壞了鏈中較早的區塊,那麼隨後所有的塊都將包含不正確的哈希值。
請花一些時間好好去理解它——這是區塊鏈設計的的核心理念。
在區塊中添加交易
我們需要一種將交易添加到塊中的方法。new_transaction() 方法可以實現這個功能,而且非常簡單:
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:
9. :param recipient:
10. :param amount:
11. :return:
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()將交易添加到列表之後,它將返回這個交易會被添加到下一個塊的索引。這對稍後提交交易的用戶有用。
創建新區塊
當 區塊鏈被實例化時,需要將它與一個沒有前輩的創世區塊一起連接起來。我們還需要向我們的創世區塊添加一個“證明”,這是挖礦的結果。
除了在我們的構造函數中創建創世區塊之外,我們還將為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:
19. :param previous_hash: (Optional)
20. :return:
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:
42. :param recipient:
43. :param amount:
44. :return:
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:
64. :return:
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 的代碼化表現。但新的區塊是如何被創建、挖掘的?
理解PoW工作量證明
工作量證明,也就是新的區塊如何在 Blockchain 上被創建或挖掘出來。它的目標是發現一個解決問題的數字,這個數字一定很難找到,但卻很容易被驗證——在網絡上的任何人都可以通過計算來驗證,這是工作證明PoW背後的核心思想。
我們來看一個非常簡單的例子:我們想找到這樣一個數值,將整數x與另一個數值y的乘積進行hash運算,使得運算的結果是一串字符串的結尾必須是數字0 。用數學表達式表示出來就是:
hash(x * y) = ac23dc…0
我們假定x = 5。在Python中實現,代碼如下:
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結尾的:
1. hash(5 * 21) = 1253e9373e...5e3600155e860
在比特幣中,工作量證明被稱為Hashcash 。它和上面舉出的簡單例子基本沒有太大區別。這是為了創建一個新的區塊,礦工們競相解決問題的算法。一般來說,難度取決於字符串中搜索的字符數。
礦工會因為在一個交易中找到了那個難題的解,而獲得系統給出的激勵:該網絡的一定量的數字貨幣。該網絡能夠很容易地驗證他們的解是否正確。
實現基本的工作量證明
為區塊鏈實現一個類似的算法,規則與上面類似:找到一個數字p,當與上一個區塊的解進行哈希運算時,產生一個前4位都是0的哈希值。
為了調整算法的難度,我們可以修改前幾位零的個數。但4個0就足夠了。你將發現,添加一個前導零就會對找到解所需的時間造成很大的不同。
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:
18. :return:
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:
33. :param proof:
34. :return:
35. """
36.
37. guess = f'{last_proof}{proof}'.encode()
38. guess_hash = hashlib.sha256(guess).hexdigest()
39. return guess_hash[:4] == "0000"
我們的類接近完成,我們已經準備好使用HTTP請求開始與它交互。
第二步:將區塊鏈作為API使用起來
使用Python的Flask框架。它是一個微型框架,它可以很容易地將端點映射到Python函數。這讓我們使用HTTP請求在web上與 Blockchain 進行交互。
我們將創建三個方法:
/transactions/new 創建一個新的交易到一個區塊。
/mine 告訴我們的服務器去挖掘一個新的區塊。
/chain 返回完整的 Blockchain 。
設置Flask
我們的“服務器”將在 Blockchain 網絡中形成單獨節點,創建一些樣板代碼如下所示:
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)
關於在上面代碼中添加的內容的簡要說明如下:
Line 15: 實例化節點。
Line 18: 為我們的節點創建一個隨機名稱。
Line 21: 實例化我們的Blockchain類。
Line 24–26: 創建/mine 端點,這是一個GET請求。
Line 28–30: 創建 /transactions/new 端點,這是一個POST 請求,因為我們將向它發送數據。
Line 32–38: 創建/chain端點,它返回完整的 Blockchain 。
Line 40–41: 在端口5000上運行服務器。
交易端點
這就是交易請求的樣子。這是用戶發送給服務器的內容:
1. {
2. "sender": "my address",
3. "recipient": "someone else's address",
4. "amount": 5
5. }
由於已經有了將交易添加到區塊的類的方法,其餘的都很簡單。讓我們編寫添加交易的函數:
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. 通過增加一筆交易,獎賞給礦工(也就是我們自己)一定量的數字貨幣。
3. 通過將新區塊添加到鏈中來鍛造區塊。
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
被挖掘出來的區塊的接收者是我們節點的地址。在這裡所做的大部分工作只是與Blockchain class中的方法進行交互。在這一點上,我們已經完成了,並且可以開始與我們的 Blockchain 進行交互了。
——未完待續——
風險警示:藍狐所有文章都不構成投資推薦,投資有風險,建議對項目進行深入考察,慎重做好自己的投資決策。
閱讀更多 藍狐筆記 的文章