摘要:每個區塊所包含個基本屬性,,交易列表,工作量證明和前一個區塊的哈希值。返回整個區塊鏈。負責檢查一個鏈是否有效,具體方法是循環讀取每個區塊并驗證哈希和證明。
本篇文章由萬云團隊編譯
原文鏈接:http://mp.weixin.qq.com/s/5-O...
如需轉載請聯系萬云官方微信:萬云Wancloud
2018年的門剛打開,區塊鏈的火就燒成了火焰山。徐小平放言要擁抱區塊鏈,朋友圈刷屏不止,連上班地鐵上都能聽到區塊鏈,一夜起,區塊鏈成了茶前飯后的談資。于是乎,那個經常聽到的問題又開始抓耳撓腮:區塊鏈到底是什么鬼?關注的訂閱號不停推送“一篇文章讓你搞懂區塊鏈”,“三分鐘Get區塊鏈”等不盡相同的內容,聲音從四面八方聚焦到你耳邊。
萬云也在思考能為想了解區塊鏈的老鐵們做點什么,鑒于已有如此多區塊鏈概念普及文,此次我們不聊枯燥的概念,而是回歸區塊鏈“技術”,一步步認真教你獲得一個屬于自己區塊鏈。放心,只要你稍微懂一點技術,你就可以實現并擁有它。
|| 以下翻譯自Daniel van Flymen的《Learn Blockchains by Building One》,有所刪改。
|| 原文地址:https://hackernoon.com/learn-...
前言
概念了解:在開始前你需要知道,區塊鏈是一種按時間將數據區塊以順序相連的方式組合在一起的鏈式數據結構,并通過密碼學來保證其不可篡改和不可偽造的分布式賬本。這些區塊可以包含交易、文件以及任何你想要的數據,重要的是它們通過哈希鏈接在一起。
目標讀者:可以輕松地閱讀和編寫一些基本的Python,并且對HTTP有一些了解。
所需工具:Python 3.6+、Flask、Requests:
pip install Flask==0.12.2 requests==2.18.4
除此之外還需安裝HTTP工具,如Postman、cURL。
源代碼地址:https://github.com/dvf/blockc...
**第一步:建立區塊鏈
①實現一個Blockchain類**
打開一個你常用的文本編輯器或者IDE,新建一個blockchain.py的python文件,并創建一個Blockchain類,在構造函數中創建兩個空的隊列,一個用于存儲區塊鏈,另一個用于存儲交易。下面是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類將用來管理鏈,它會存儲交易,并且提供一些方法來幫助添加新的區塊到鏈。下面是詳細的實現方法。
每個區塊所包含5個基本屬性:index,timestamp (in Unix time),交易列表,工作量證明和前一個區塊的哈希值。我們來看一個例子:
block = { "index": 1, "timestamp": 1506057125.900785, "transactions": [ { "sender": "8527147fe1f5426f9dd545de4b27ee00", "recipient": "a77f5cdfa2934df3954a5c7c7da5df1f", "amount": 5, } ], "proof": 324984774000, "previous_hash": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" }
到這里,我們對于鏈的概念應該比較清楚了,每個新的區塊都會包含上一個區塊的哈希值,從而讓區塊鏈具有不可篡改的特性。如果攻擊者攻擊了鏈中比較靠前的區塊,則所有后面的區塊將包含不正確的哈希值。如果不能理解,慢慢消化——這是理解區塊鏈技術的核心思想。
②將交易添加到區塊
接下來我們實現一個將交易添加到區塊的方法,繼續看代碼:
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()方法中向列表中添加一筆交易之后,它返回值是本次交易的index,該index會被添加到下一個待挖掘區塊,后面在用戶提交交易時也會用到。
③建一個新的區塊
當Blockchain被實例化后,我們需要創建一個創世區塊,同時為我們的創世區塊添加一個工作量證明,這是挖礦的結果,我們稍后會詳細討論挖礦。
除了創建創世區塊的代碼,我們還需要補充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): """ 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 transactions self.current_transactions = [] self.chain.append(block) return block 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 @property def last_block(self): return self.chain[-1] @staticmethod def 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 hashes block_string = json.dumps(block, sort_keys=True).encode() return hashlib.sha256(block_string).hexdigest()
到此,我們的區塊鏈已經基本上實現了雛形。這時候,你肯定想知道新區塊是怎么被挖出來的,也就是我們通常所說的“挖礦”。
④什么是工作量證明?
想了解什么是“挖礦”,就必須理解工作量證明(POW)是什么。區塊鏈上每一個新的區塊都來自于工作量證明(POW),POW的目標是計算出一串解決問題的數字,這個結果眾所周知是很難計算的,但卻十分容易驗證,因為網絡上的任何人都能夠驗證這個結果,這是“工作量證明”背后的核心思想。
我們來看一個非常簡單的例子幫助理解:
假設整數X乘以另一個整數y的哈希值必須以0結尾,hash(x * y) = ac23dc...0. 設x = 5.求y。我們用Python來實現:
from hashlib import sha256 x = 5 y = 0 # We don"t know what y should be yet... while sha256(f"{x*y}".encode()).hexdigest()[-1] != "0": y += 1 print(f"The solution is y = {y}")
得到的答案是當y = 21,哈希值的結尾為0:
hash(5 * 21) = 1253e9373e...5e3600155e860
在比特幣中,工作證明算法被稱為Hashcash,這和我們上面所舉的例子差不多,結果難于發現卻易于校驗。Hashcash是礦工為了創建一個新區塊而爭相計算的問題,計算難度通常取決于字符串中搜索的字符數,通常也會花費一定的時間才能計算得到,最終計算出來的礦工們會通過交易獲得一定數量的比特幣作為獎勵。
⑤實現一個基本的工作量證明
首先我們為Blockchain類實現一個類似的算法:
規則:找到一個數字p,使得它與前一個區塊的 proof 拼接成的字符串的 Hash 值以 4 個零開頭。
import hashlib import json from time import time from uuid import uuid4 class 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 = 0 while self.valid_proof(last_proof, proof) is False: proof += 1 return proof @staticmethod def 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"
通過修改前導零的數量,可以調整算法的難度,但是4個零完全足夠了。你會發現,每當增加一個前導零,找到一個對應的解決方案與所需的時間之間會產生巨大的差異。
進行到這里,我們的Blockchain類已經基本完成,接下來我們實現HTTP服務進行交互。
第二步:區塊鏈API
我們將使用Python Flask框架,Flask是一個輕量級的Web應用框架,這使我們可以通過web服務來調用Blockchian類。
①創建三個API:
?/ transactions / new為區塊創建一個新的交易
?/mine告訴我們的服務器開采新的區塊。
?/chain返回整個區塊鏈。
②使用Flask
我們的“服務器”將基于Flask框架來實現區塊鏈網絡中的一個節點。 我們來添加一些模板代碼:
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)
以下是對上面添加的內容的進行簡要說明:
Line15:實例化Flask web服務節點。 Line18:為我們的服務節點創建一個隨機的名稱。 Line21:實例化Blockchain類。 Line24-26:創建一個路由為/mine的GET請求的,調用后端Blockchain的new block方法。 Line28-30:創建一個路由為/transactions/new的POST請求,將數據發送給后端Blockchina的new transaction方法。 Line32-38:創建一個路由為/chain的GET請求,將返回整個鏈。 Line40-41:在端口5000上運行服務器。
③實現交易
下面是用戶發起交易時發送到服務器的請求:
{ "sender": "my address", "recipient": "someone else"s address", "amount": 5 }
由于我們已經有了將交易添加到區塊的方法,接下去就十分容易了。
下面我們來實現添加交易的函數:
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
④實現挖礦
我們的挖礦方法是魔法發生的地方。它十分容易,只做三件事情:計算工作量證明;通過新增一筆交易獎勵礦工一定數量的比特幣;創建新的區塊并將其添加到鏈中來。
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) # 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 chain previous_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
需要注意的是,開采塊的交易接收者是我們自己服務器節點的地址,我們在這里所做的大部分工作只是與Blockchain類進行交互,基于以上我們區塊鏈已經完成了,接下來開始交互演示。
第三步:交互演示
您可以使用cURL或Postman與API進行交互。
啟動服務器:
$ python blockchain.py * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
嘗試通過向http:// localhost:5000 / mine發出GET請求來挖掘區塊:
創建一個新的交易,向http://localhost:5000/transactions/new發出一個POST請求:
也可以使用cURL發送請求:
$ curl -X POST -H "Content-Type: application/json" -d "{ "sender": "d4ee26eee15148ee92c6cd394edd974e", "recipient": "someone-other-address", "amount": 5 }" "http://localhost:5000/transactions/new"
以上僅為交互演示的示例,你可以在你自己所完成的區塊鏈上進行更多嘗試。
第四步:共識機制
我們有一個基本的區塊鏈可以進行交易和挖礦,但其實區塊鏈更重要的意義在于它們是分布式的。那么我們需要確保所有的節點都運行在同一條鏈上,這就是回歸到了共識問題,如果要滿足在網絡上有多個節點并且不斷增加,我們必須要實現共識算法。
①注冊新的節點
在實現共識算法之前,需要找到一種方式讓網絡上的節點知道其相鄰的節點,每個節點都需要存儲網絡上其他節點的記錄。因此,我們需要新增幾個方法來幫助實現:
1./nodes/register接受URL形式的新節點列表。
/ nodes / resolve來執行我們的共識算法,它可以解決任何沖突,確保節點具有正確的鏈。
下面我們將修改Blockchain的構造函數以提供注冊節點的方法:
... 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)
注意,我們使用set()集合來保存節點列表,這是確保新節點的添加是冪等的簡便方法,這意味著無論我們添加特定節點多少次,它都只會出現一次。
②實現共識算法
如前所述,當一個節點與另一個節點有不同時會發生沖突,為了解決這個問題,我們遵循取最長鏈原則,通過使用此算法,讓網絡中的節點間達成共識。
... import requests class 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 = 1 while current_index < len(chain): block = chain[current_index] print(f"{last_block}") print(f"{block}") print(" ----------- ") # 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): """ This is our Consensus Algorithm, it resolves conflicts by replacing our chain with the longest one in the network. :return: True if our chain was replaced, False if not """ 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
valid_chain()負責檢查一個鏈是否有效,具體方法是循環讀取每個區塊并驗證哈希和證明。
resolve_conflicts()負責循環讀取所有相鄰節點,獲取它們的鏈并使用上面的方法驗證它們的有效性。如果找到了一個更長的有效鏈,則取代我們當前的鏈。
我們將兩個方法注冊到我們的API中,一個用于添加相鄰節點,另一個用于解決沖突:
@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
最后,如果你愿意的話可以開啟另一臺機器,并在你的網絡上運轉不同的節點。或者使用同一臺機器上的不同端口啟動進程。我在我的機器的不同端口創建另外一個節點,并將其注冊到當前區塊鏈網絡中。 因此,我有兩個節點:http:// localhost:5000和http:// localhost:5001。
為了確保鏈更長,我在節點2上挖掘了一些新的區塊。 之后,我在節點1上調用GET / nodes / resolve,此處的鏈已經被共識算法計算后的得到的新鏈所替代。
現在,你可以邀請一些朋友來一起測試你的區塊鏈了。
本文教程到此結束,那么,屬于你自己的區塊鏈擼好了嗎?別忘了分享給身邊同為程序員的朋友,一起來擼區塊鏈!
本篇文章由萬云團隊編譯,如需轉載請聯系萬云官方微信:萬云Wancloud
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41404.html
摘要:每個區塊所包含個基本屬性,,交易列表,工作量證明和前一個區塊的哈希值。返回整個區塊鏈。負責檢查一個鏈是否有效,具體方法是循環讀取每個區塊并驗證哈希和證明。 本篇文章由萬云團隊編譯原文鏈接:http://mp.weixin.qq.com/s/5-O...如需轉載請聯系萬云官方微信:萬云Wancloud 2018年的門剛打開,區塊鏈的火就燒成了火焰山。徐小平放言要擁抱區塊鏈,朋友圈刷屏不止...
摘要:但是如果將我們的包還沒有測試好,直接發到遠程未免顯得有點笨拙。發包創建文件在發包之前排除一些沒有必要發的文件注冊,就按照提示依次填寫信息就好了發布由于本人學識有限,有很多需要提升的地方,望大家多多指教。 創建一個項目目錄 mkdir project 創建package.json npm init 配置tsconfig.json npm -i typescript -g //全局安...
摘要:最后提醒下,代碼中使用而非的原因是為了啟動移動端手機的動畫加速,提升動畫流暢度。 前言 最近面試發現很多前端程序員都從來沒有寫過插件的經驗,基本上都是網上百度。所以打算寫一系列文章,手把手的教一些沒有寫過組件的兄弟們如何去寫插件。本系列文章都基于VUE,核心內容都一樣,會了之后大家可以快速的改寫成react、angular或者是小程序等組件。這篇文章是第一篇,寫的是一個類似QQ的側邊菜...
摘要:夾在中間的被鏈式調用,他們拿到上個的返回值,為下一個提供輸入。最終把返回值和傳給。前面我們說過,也是一個模塊,它導出一個函數,該函數的參數是的源模塊,處理后把返回值交給下一個。 文:小 boy(滬江網校Web前端工程師)本文原創,轉載請注明作者及出處 showImg(https://segmentfault.com/img/remote/1460000012990131?w=1083...
摘要:畫字首先我在畫布上畫了個點,用這些點來組成我們要顯示的字,用不到的字就隱藏起來。星星閃爍效果這個效果實現很簡單,就是讓星星不停的震動,具體就是讓點的目的地坐標不停的進行小范圍的偏移。 哈哈哈哈!!!當我說在寫這邊文章的時候,妹子已經追到了,哈哈哈哈哈!!! 其實東西是一年前寫的,妹子早就追到手了,當時就是用這個東西來表白的咯,二話不說,先看效果(點擊屏幕可顯示下一句) showImg(...
閱讀 1824·2021-09-28 09:46
閱讀 3143·2019-08-30 14:22
閱讀 1878·2019-08-26 13:36
閱讀 3343·2019-08-26 11:32
閱讀 2081·2019-08-23 16:56
閱讀 1151·2019-08-23 16:09
閱讀 1303·2019-08-23 12:55
閱讀 2148·2019-08-23 11:44