新聞中心
了解區(qū)塊鏈是如何工作的最快的方法是構建一個。

在承德等地區(qū),都構建了全面的區(qū)域性戰(zhàn)略布局,加強發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務理念,為客戶提供成都網(wǎng)站制作、網(wǎng)站設計、外貿(mào)網(wǎng)站建設 網(wǎng)站設計制作按需設計,公司網(wǎng)站建設,企業(yè)網(wǎng)站建設,高端網(wǎng)站設計,成都全網(wǎng)營銷推廣,外貿(mào)營銷網(wǎng)站建設,承德網(wǎng)站建設費用合理。
你看到這篇文章是因為和我一樣,對加密貨幣的大熱而感到興奮。并且想知道區(qū)塊鏈是如何工作的 —— 它們背后的技術基礎是什么。
但是理解區(qū)塊鏈并不容易 —— 至少對我來說是這樣。我徜徉在各種難懂的視頻中,并且因為示例太少而陷入深深的挫敗感中。
我喜歡在實踐中學習。這會使得我在代碼層面上處理主要問題,從而可以讓我堅持到底。如果你也是這么做的,在本指南結束的時候,你將擁有一個功能正常的區(qū)塊鏈,并且實實在在地理解了它的工作原理。
開始之前 …
記住,區(qū)塊鏈是一個 不可更改的、有序的 記錄(被稱為區(qū)塊)的鏈。它們可以包括交易transaction、文件或者任何你希望的真實數(shù)據(jù)。最重要的是它們是通過使用哈希鏈接到一起的。
如果你不知道哈希是什么,這里有解釋。
本指南的目標讀者是誰? 你應該能輕松地讀、寫一些基本的 Python 代碼,并能夠理解 HTTP 請求是如何工作的,因為我們討論的區(qū)塊鏈將基于 HTTP。
我需要做什么? 確保安裝了 Python 3.6+(以及 pip),還需要去安裝 Flask 和非常好用的 Requests 庫:
pip install Flask==0.12.2 requests==2.18.4
當然,你也需要一個 HTTP 客戶端,像 Postman 或者 cURL。哪個都行。
最終的代碼在哪里可以找到? 源代碼在 這里。
第 1 步:構建一個區(qū)塊鏈
打開你喜歡的文本編輯器或者 IDE,我個人喜歡 PyCharm。創(chuàng)建一個名為 blockchain.py 的新文件。我將僅使用一個文件,如果你看暈了,可以去參考 源代碼。
描述一個區(qū)塊鏈
我們將創(chuàng)建一個 Blockchain 類,它的構造函數(shù)將去初始化一個空列表(去存儲我們的區(qū)塊鏈),以及另一個列表去保存交易。下面是我們的類規(guī)劃:
class Blockchain(object):def __init__(self):self.chain = []self.current_transactions = []def new_block(self):# Creates a new Block and adds it to the chainpassdef new_transaction(self):# Adds a new transaction to the list of transactionspass@staticmethoddef hash(block):# Hashes a Blockpass@propertydef last_block(self):# Returns the last Block in the chainpass
我們的 Blockchain 類的原型
我們的 Blockchain 類負責管理鏈。它將存儲交易并且有一些為鏈中增加新區(qū)塊的輔助性質的方法。現(xiàn)在我們開始去充實一些類的方法。
區(qū)塊是什么樣子的?
每個區(qū)塊有一個索引、一個時間戳(Unix 時間)、一個交易的列表、一個證明(后面會詳細解釋)、以及前一個區(qū)塊的哈希。
單個區(qū)塊的示例應該是下面的樣子:
block = {'index': 1,'timestamp': 1506057125.900785,'transactions': [{'sender': "8527147fe1f5426f9dd545de4b27ee00",'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",'amount': 5,}],'proof': 324984774000,'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"}
我們的區(qū)塊鏈中的塊示例
此刻,鏈的概念應該非常明顯 —— 每個新區(qū)塊包含它自身的信息和前一個區(qū)域的哈希。這一點非常重要,因為這就是區(qū)塊鏈不可更改的原因:如果攻擊者修改了一個早期的區(qū)塊,那么所有的后續(xù)區(qū)塊將包含錯誤的哈希。
這樣做有意義嗎?如果沒有,就讓時間來埋葬它吧 —— 這就是區(qū)塊鏈背后的核心思想。
添加交易到一個區(qū)塊
我們將需要一種區(qū)塊中添加交易的方式。我們的 new_transaction() 就是做這個的,它非常簡單明了:
class Blockchain(object):...def new_transaction(self, sender, recipient, amount):"""Creates a new transaction to go into the next mined Block:param sender:Address of the Sender :param recipient:Address of the Recipient :param amount:Amount :return: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
在 new_transaction() 運行后將在列表中添加一個交易,它返回添加交易后的那個區(qū)塊的索引 —— 那個區(qū)塊接下來將被挖礦。提交交易的用戶后面會用到這些。
創(chuàng)建新區(qū)塊
當我們的 Blockchain 被實例化后,我們需要一個創(chuàng)世區(qū)塊(一個沒有祖先的區(qū)塊)來播種它。我們也需要去添加一些 “證明” 到創(chuàng)世區(qū)塊,它是挖礦(工作量證明 PoW)的成果。我們在后面將討論更多挖礦的內(nèi)容。
除了在我們的構造函數(shù)中創(chuàng)建創(chuàng)世區(qū)塊之外,我們還需要寫一些方法,如 new_block()、new_transaction() 以及 hash():
import hashlibimport jsonfrom time import timeclass Blockchain(object):def __init__(self):self.current_transactions = []self.chain = []# Create the genesis blockself.new_block(previous_hash=1, proof=100)def new_block(self, proof, previous_hash=None):"""Create a new Block in the Blockchain:param proof:The proof given by the Proof of Work algorithm :param previous_hash: (Optional)Hash of previous Block :return: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 transactionsself.current_transactions = []self.chain.append(block)return blockdef new_transaction(self, sender, recipient, amount):"""Creates a new transaction to go into the next mined Block:param sender:Address of the Sender :param recipient:Address of the Recipient :param amount:Amount :return: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@propertydef last_block(self):return self.chain[-1]@staticmethoddef hash(block):"""Creates a SHA-256 hash of a Block:param block:Block :return:"""# We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashesblock_string = json.dumps(block, sort_keys=True).encode()return hashlib.sha256(block_string).hexdigest()
上面的內(nèi)容簡單明了 —— 我添加了一些注釋和文檔字符串,以使代碼清晰可讀。到此為止,表示我們的區(qū)塊鏈基本上要完成了。但是,你肯定想知道新區(qū)塊是如何被創(chuàng)建、打造或者挖礦的。
理解工作量證明
工作量證明Proof of Work(PoW)算法是在區(qū)塊鏈上創(chuàng)建或者挖出新區(qū)塊的方法。PoW 的目標是去撞出一個能夠解決問題的數(shù)字。這個數(shù)字必須滿足“找到它很困難但是驗證它很容易”的條件 —— 網(wǎng)絡上的任何人都可以計算它。這就是 PoW 背后的核心思想。
我們來看一個非常簡單的示例來幫助你了解它。
我們來解決一個問題,一些整數(shù) x 乘以另外一個整數(shù) y 的結果的哈希值必須以 0 結束。因此,hash(x * y) = ac23dc…0。為簡單起見,我們先把 x = 5 固定下來。在 Python 中的實現(xiàn)如下:
from hashlib import sha256x = 5y = 0 # We don't know what y should be yet...while sha256(f'{x*y}'.encode()).hexdigest()[-1] != "0":y += 1print(f'The solution is y = {y}')
在這里的答案是 y = 21。因為它產(chǎn)生的哈希值是以 0 結尾的:
hash(5 * 21) = 1253e9373e...5e3600155e860
在比特幣中,工作量證明算法被稱之為 Hashcash。與我們上面的例子沒有太大的差別。這就是礦工們進行競賽以決定誰來創(chuàng)建新塊的算法。一般來說,其難度取決于在一個字符串中所查找的字符數(shù)量。然后礦工會因其做出的求解而得到獎勵的幣——在一個交易當中。
網(wǎng)絡上的任何人都可以很容易地去核驗它的答案。
實現(xiàn)基本的 PoW
為我們的區(qū)塊鏈來實現(xiàn)一個簡單的算法。我們的規(guī)則與上面的示例類似:
找出一個數(shù)字
p,它與前一個區(qū)塊的答案進行哈希運算得到一個哈希值,這個哈希值的前四位必須是由0組成。
import hashlibimport jsonfrom time import timefrom uuid import uuid4class Blockchain(object):...def proof_of_work(self, last_proof):"""Simple Proof of Work Algorithm:- Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'- p is the previous proof, and p' is the new proof:param last_proof::return:"""proof = 0while self.valid_proof(last_proof, proof) is False:proof += 1return proof@staticmethoddef valid_proof(last_proof, proof):"""Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?:param last_proof:Previous Proof :param proof:Current Proof :return:True if correct, False if not. """guess = f'{last_proof}{proof}'.encode()guess_hash = hashlib.sha256(guess).hexdigest()return guess_hash[:4] == "0000"
為了調(diào)整算法的難度,我們可以修改前導 0 的數(shù)量。但是 4 個零已經(jīng)足夠難了。你會發(fā)現(xiàn),將前導 0 的數(shù)量每增加一,那么找到正確答案所需要的時間難度將大幅增加。
我們的類基本完成了,現(xiàn)在我們開始去使用 HTTP 請求與它交互。
第 2 步:以 API 方式去訪問我們的區(qū)塊鏈
我們將使用 Python Flask 框架。它是個微框架,使用它去做端點到 Python 函數(shù)的映射很容易。這樣我們可以使用 HTTP 請求基于 web 來與我們的區(qū)塊鏈對話。
我們將創(chuàng)建三個方法:
/transactions/new在一個區(qū)塊上創(chuàng)建一個新交易/mine告訴我們的服務器去挖礦一個新區(qū)塊/chain返回完整的區(qū)塊鏈
配置 Flask
我們的 “服務器” 將在我們的區(qū)塊鏈網(wǎng)絡中產(chǎn)生一個單個的節(jié)點。我們來創(chuàng)建一些樣板代碼:
import hashlibimport jsonfrom textwrap import dedentfrom time import timefrom uuid import uuid4from flask import Flaskclass Blockchain(object):...# Instantiate our Nodeapp = Flask(__name__)# Generate a globally unique address for this nodenode_identifier = str(uuid4()).replace('-', '')# Instantiate the Blockchainblockchain = 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), 200if __name__ == '__main__':app.run(host='0.0.0.0', port=5000)
對上面的代碼,我們做添加一些詳細的解釋:
- Line 15:實例化我們的節(jié)點。更多關于 Flask 的知識讀 這里。
- Line 18:為我們的節(jié)點創(chuàng)建一個隨機的名字。
- Line 21:實例化我們的區(qū)塊鏈類。
- Line 24–26:創(chuàng)建
/mine端點,這是一個 GET 請求。 - Line 28–30:創(chuàng)建
/transactions/new端點,這是一個 POST 請求,因為我們要發(fā)送數(shù)據(jù)給它。 - Line 32–38:創(chuàng)建
/chain端點,它返回全部區(qū)塊鏈。 - Line 40–41:在 5000 端口上運行服務器。
交易端點
這就是對一個交易的請求,它是用戶發(fā)送給服務器的:
{"sender": "my address","recipient": "someone else's address","amount": 5}
因為我們已經(jīng)有了添加交易到塊中的類方法,剩下的就很容易了。讓我們寫個函數(shù)來添加交易:
import hashlibimport jsonfrom textwrap import dedentfrom time import timefrom uuid import uuid4from 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 datarequired = ['sender', 'recipient', 'amount']if not all(k in values for k in required):return 'Missing values', 400# Create a new Transactionindex = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])response = {'message': f'Transaction will be added to Block {index}'}return jsonify(response), 201
創(chuàng)建交易的方法
挖礦端點
我們的挖礦端點是見證奇跡的地方,它實現(xiàn)起來很容易。它要做三件事情:
- 計算工作量證明
- 因為礦工(我們)添加一個交易而獲得報酬,獎勵礦工(我們) 1 個幣
- 通過將它添加到鏈上而打造一個新區(qū)塊
import hashlibimport jsonfrom time import timefrom uuid import uuid4from 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_blocklast_proof = last_block['proof']proof = blockchain.proof_of_work(last_proof)# We must receive a reward for finding the proof.# The sender is "0" to signify that this node has mined a new coin.blockchain.new_transaction(sender="0",recipient=node_identifier,amount=1,)# Forge the new Block by adding it to the chainprevious_hash = blockchain.hash(last_block)block = blockchain.new_block(proof, previous_hash)response = {'message': "New Block Forged",'index': block['index'],'transactions': block['transactions'],'proof': block['proof'],'previous_hash': block['previous_hash'],}return jsonify(response), 200
注意,挖掘出的區(qū)塊的接收方是我們的節(jié)點地址?,F(xiàn)在,我們所做的大部分工作都只是與我們的 Blockchain 類的方法進行交互的。到目前為止,我們已經(jīng)做完了,現(xiàn)在開始與我們的區(qū)塊鏈去交互。
第 3 步:與我們的區(qū)塊鏈去交互
你可以使用簡單的 cURL 或者 Postman 通過網(wǎng)絡與我們的 API 去交互。
啟動服務器:
$ python blockchain.py* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
我們通過生成一個 GET 請求到 http://localhost:5000/mine 去嘗試挖一個區(qū)塊:
使用 Postman 去生成一個 GET 請求
我們通過生成一個 POST 請求到 http://localhost:5000/transactions/new 去創(chuàng)建一個區(qū)塊,請求數(shù)據(jù)包含我們的交易結構:
使用 Postman 去生成一個 POST 請求
如果你不使用 Postman,也可以使用 cURL 去生成一個等價的請求:
$ curl -X POST -H "Content-Type: application/json" -d '{"sender": "d4ee26eee15148ee92c6cd394edd974e","recipient": "someone-other-address","amount": 5}' "http://localhost:5000/transactions/new"
我重啟動我的服務器,然后我挖到了兩個區(qū)塊,這樣總共有了 3 個區(qū)塊。我們通過請求 http://localhost:5000/chain 來檢查整個區(qū)塊鏈:
{"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}
第 4 步:共識
這是很酷的一個地方。我們已經(jīng)有了一個基本的區(qū)塊鏈,它可以接收交易并允許我們?nèi)ネ诰虺鲂聟^(qū)塊。但是區(qū)塊鏈的整個重點在于它是去中心化的decentralized。而如果它們是去中心化的,那我們?nèi)绾尾拍艽_保它們表示在同一個區(qū)塊鏈上?這就是共識Consensus問題,如果我們希望在我們的網(wǎng)絡上有多于一個的節(jié)點運行,那么我們將必須去實現(xiàn)一個共識算法。
注冊新節(jié)點
在我們能實現(xiàn)一個共識算法之前,我們需要一個辦法去讓一個節(jié)點知道網(wǎng)絡上的鄰居節(jié)點。我們網(wǎng)絡上的每個節(jié)點都保留有一個該網(wǎng)絡上其它節(jié)點的注冊信息。因此,我們需要更多的端點:
/nodes/register以 URL 的形式去接受一個新節(jié)點列表/nodes/resolve去實現(xiàn)我們的共識算法,由它來解決任何的沖突 —— 確保節(jié)點有一個正確的鏈。
我們需要去修改我們的區(qū)塊鏈的構造函數(shù),來提供一個注冊節(jié)點的方法:
...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:Address of node. Eg. 'http://192.168.0.5:5000' :return: None"""parsed_url = urlparse(address)self.nodes.add(parsed_url.netloc)
一個添加鄰居節(jié)點到我們的網(wǎng)絡的方法
注意,我們將使用一個 set() 去保存節(jié)點列表。這是一個非常合算的方式,它將確保添加的節(jié)點是冪等idempotent的 —— 這意味著不論你將特定的節(jié)點添加多少次,它都是精確地只出現(xiàn)一次。
實現(xiàn)共識算法
正如前面提到的,當一個節(jié)點與另一個節(jié)點有不同的鏈時就會產(chǎn)生沖突。為解決沖突,我們制定一個規(guī)則,即最長的有效的鏈才是權威的鏈。換句話說就是,網(wǎng)絡上最長的鏈就是事實上的區(qū)塊鏈。使用這個算法,可以在我們的網(wǎng)絡上節(jié)點之間達到共識。
...import requestsclass Blockchain(object)...def valid_chain(self, chain):"""Determine if a given blockchain is valid:param chain:A blockchain
:return:True if valid, False if not """last_block = chain[0]current_index = 1while 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 correctif block['previous_hash'] != self.hash(last_block):return False# Check that the Proof of Work is correctif not self.valid_proof(last_block['proof'], block['proof']):return Falselast_block = blockcurrent_index += 1return Truedef resolve_conflicts(self):"""This is our Consensus Algorithm, it resolves conflictsby replacing our chain with the longest one in the network.:return:True if our chain was replaced, False if not """neighbours = self.nodesnew_chain = None# We're only looking for chains longer than oursmax_length = len(self.chain)# Grab and verify the chains from all the nodes in our networkfor 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 validif length > max_length and self.valid_chain(chain):max_length = lengthnew_chain = chain# Replace our chain if we discovered a new, valid chain longer than oursif new_chain:self.chain = new_chainreturn Truereturn False
第一個方法 valid_chain() 是負責來檢查鏈是否有效,它通過遍歷區(qū)塊鏈上的每個區(qū)塊并驗證它們的哈希和工作量證明來檢查這個區(qū)塊鏈是否有效。
resolve_conflicts() 方法用于遍歷所有的鄰居節(jié)點,下載它們的鏈并使用上面的方法去驗證它們是否有效。如果找到有效的鏈,確定誰是最長的鏈,然后我們就用最長的鏈來替換我們的當前的鏈。
在我們的 API 上來注冊兩個端點,一個用于添加鄰居節(jié)點,另一個用于解決沖突:
@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", 400for 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
這種情況下,如果你愿意,可以使用不同的機器來做,然后在你的網(wǎng)絡上啟動不同的節(jié)點?;蛘呤窃谕慌_機器上使用不同的端口啟動另一個進程。我是在我的機器上使用了不同的端口啟動了另一個節(jié)點,并將它注冊到了當前的節(jié)點上。因此,我現(xiàn)在有了兩個節(jié)點:http://localhost:5000 和 http://localhost:5001。
注冊一個新節(jié)點
我接著在節(jié)點 2 上挖出一些新區(qū)塊,以確保這個鏈是最長的。之后我在節(jié)點 1 上以 GET 方式調(diào)用了 /nodes/resolve,這時,節(jié)點 1 上的鏈被共識算法替換成節(jié)點 2 上的鏈了:
工作中的共識算法
然后將它們封裝起來 … 找一些朋友來幫你一起測試你的區(qū)塊鏈。
我希望以上內(nèi)容能夠鼓舞你去創(chuàng)建一些新的東西。我是加密貨幣的狂熱擁護者,因此我相信區(qū)塊鏈將迅速改變我們對經(jīng)濟、政府和記錄保存的看法。
更新: 我正計劃繼續(xù)它的第二部分,其中我將擴展我們的區(qū)塊鏈,使它具備交易驗證機制,同時討論一些你可以在其上產(chǎn)生你自己的區(qū)塊鏈的方式。(LCTT 譯注:第二篇并沒有~?。?/p>
網(wǎng)站名稱:想學習區(qū)塊鏈?那就用Python構建一個
網(wǎng)站地址:http://www.fisionsoft.com.cn/article/djhdjgc.html


咨詢
建站咨詢
