原文鏈接:https://medium.com/@vanflymen/learn-blockchains-by-building-one-117428612f46
- 交易Transaction
- 區(qū)塊Block
- 工作量證明
你來這里是因?yàn)椋臀乙粯幼皆椋銓?duì)加密貨幣的崛起感到興奮官研。你想知道區(qū)塊鏈?zhǔn)侨绾喂ぷ鞯摹鼈儽澈蟮幕炯夹g(shù)惠勒。
但理解區(qū)塊鏈并不容易——至少對(duì)我來說不是。我艱難地瀏覽了密集的視頻,學(xué)習(xí)了很多的教程币励,并處理了由于例子太少而產(chǎn)生的放大的挫折感。
我喜歡邊做邊學(xué)珊拼。如果您也這樣做食呻,在本指南的最后,您將擁有一個(gè)功能良好的區(qū)塊鏈澎现,并對(duì)它們的工作原理有一個(gè)堅(jiān)實(shí)的了解仅胞。
在你開始之前…
請(qǐng)記住,區(qū)塊鏈?zhǔn)且粋€(gè)不可變的剑辫、連續(xù)的記錄鏈干旧,稱為塊。它們可以包含事務(wù)妹蔽、文件或任何您喜歡的數(shù)據(jù)椎眯。但重要的是它們是用散列連接在一起的挠将。
如果你不確定哈希是什么,這里有一個(gè)解釋编整。
這本指南是針對(duì)誰的?
您應(yīng)該能夠輕松地閱讀和編寫一些基本的Python捐名,并對(duì)HTTP請(qǐng)求的工作原理有一些了解,因?yàn)槲覀儗⑼ㄟ^HTTP與我們的區(qū)塊鏈通信闹击。我需要什么?
確保安裝了Python 3.6+(以及pip)镶蹋。你還需要安裝Flask和wonderful Requests 庫(kù):
pip install Flask==0.12.2 requests==2.18.4
您還需要一個(gè)HTTP客戶機(jī),比如Postman
或cURL;但什么都行赏半。
- 最終代碼在哪里?
這里提供了源代碼贺归。
步驟1:構(gòu)建一個(gè)區(qū)塊鏈
打開您最喜歡的文本編輯器或IDE,我個(gè)人喜歡PyCharm。創(chuàng)建一個(gè)名為blockchain.py的新文件断箫。我們只使用一個(gè)文件拂酣,但是如果您丟失了,您可以隨時(shí)查閱源代碼仲义。
代表一個(gè)區(qū)塊鏈
我們將創(chuàng)建一個(gè)區(qū)塊鏈類婶熬,該類的構(gòu)造函數(shù)創(chuàng)建一個(gè)初始空列表(用于存儲(chǔ)我們的區(qū)塊鏈),另一個(gè)用于存儲(chǔ)事務(wù)埃撵。這是我們班的藍(lán)圖:
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
我們的區(qū)塊鏈類負(fù)責(zé)管理鏈赵颅。它將存儲(chǔ)事務(wù),并具有一些用于向鏈添加新塊的輔助方法暂刘。讓我們開始充實(shí)一些方法饺谬。
Block是什么樣子的?
每個(gè)塊都有以下內(nèi)容:
- 一個(gè)索引、
- 一個(gè)時(shí)間戳(Unix時(shí)間)谣拣、
- 一個(gè)事務(wù)列表募寨、
- 一個(gè)證明(稍后將詳細(xì)介紹)
- 前一個(gè)塊的散列。
下面是單個(gè)塊的例子:
block = {
'index': 1,
'timestamp': 1506057125.900785,
'transactions': [
{
'sender': "8527147fe1f5426f9dd545de4b27ee00",
'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",
'amount': 5,
}
],
'proof': 324984774000,
'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824”
}
此時(shí)森缠,鏈的概念應(yīng)該很明顯——每個(gè)新塊都包含前一個(gè)塊的散列(Hash)拔鹰。這是至關(guān)重要的,因?yàn)樗箙^(qū)塊鏈具有不變性:如果攻擊者破壞了鏈中較早的一個(gè)區(qū)塊贵涵,那么所有后續(xù)的區(qū)塊都將包含不正確的散列(Hash)列肢。
這說得通嗎?如果沒有,花點(diǎn)時(shí)間讓它沉下去——這是區(qū)塊鏈背后的核心理念独悴。
將事務(wù)添加到塊中
我們需要一種向塊添加事務(wù)的方法例书。我們的new_transaction()方法對(duì)此負(fù)責(zé),它非常簡(jiǎn)單:
class Blockchain(object):
...
def new_transaction(self, sender, recipient, amount):
"""
Creates a new transaction to go into the next mined Block
: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
new_transaction()將一個(gè)事務(wù)添加到列表后刻炒,它返回將事務(wù)添加到下一個(gè)要挖掘的塊的索引决采。這將在稍后對(duì)提交事務(wù)的用戶有用。
創(chuàng)建新的Block
當(dāng)我們的區(qū)塊鏈被實(shí)例化時(shí)坟奥,我們需要用一個(gè)genesis塊來播種它——一個(gè)沒有前處理的塊树瞭。我們還需要向genesis塊添加一個(gè)“證明”拇厢,這是挖掘(或工作證明)的結(jié)果。稍后我們將更多地討論采礦晒喷。
除了在構(gòu)造函數(shù)中創(chuàng)建genesis塊孝偎,我們還將充實(shí)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: <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):
"""
Creates a new transaction to go into the next mined Block
: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):
"""
Creates a SHA-256 hash of a Block
: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()
上面的內(nèi)容應(yīng)該是直接了當(dāng)?shù)摹姨砑恿艘恍┳⑨尯臀臋n字符串來幫助保持清晰凉敲。我們幾乎完成了對(duì)區(qū)塊鏈的表示衣盾。但此時(shí),您一定想知道如何創(chuàng)建爷抓、鍛造或挖掘新的塊势决。
工作證明
工作算法(PoW)的一個(gè)證明是如何在區(qū)塊鏈上創(chuàng)建或挖掘新的塊。PoW的目標(biāo)是發(fā)現(xiàn)一個(gè)可以解決問題的數(shù)蓝撇。這個(gè)數(shù)字一定很難找到果复,但是很容易被網(wǎng)絡(luò)上的任何人驗(yàn)證——從計(jì)算的角度來說。這是工作證明背后的核心思想渤昌。
我們將看一個(gè)非常簡(jiǎn)單的例子來幫助理解這一點(diǎn)虽抄。
讓我們決定某個(gè)整數(shù)x乘以另一個(gè)y的散列必須以0結(jié)尾。哈希(x * y) = ac23dc…0独柑。對(duì)于這個(gè)簡(jiǎn)化的例子迈窟,我們令x = 5。用Python實(shí)現(xiàn):
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群嗤。因?yàn)椴ぢ。傻纳⒘幸?結(jié)尾:
hash(5 * 21) = 1253e9373e…5e3600155e860
在比特幣中兵琳,工作證明算法被稱為Hashcash狂秘。它和上面的基本例子沒有太大的不同躯肌。它是礦工們?yōu)榱藙?chuàng)建一個(gè)新的塊而競(jìng)相求解的算法。通常钱烟,難度由字符串中搜索的字符數(shù)量決定。然后嫡丙,這些礦商會(huì)因?yàn)樗麄兊慕鉀Q方案而獲得一筆交易中的硬幣作為回報(bào)拴袭。
網(wǎng)絡(luò)能夠很容易地驗(yàn)證他們的解決方案曙博。
實(shí)現(xiàn)基本的工作證明
讓我們?yōu)閰^(qū)塊鏈實(shí)現(xiàn)一個(gè)類似的算法。我們的規(guī)則將類似于上面的例子:
找到一個(gè)數(shù)字p父泳,當(dāng)它與前一個(gè)塊的解進(jìn)行散列時(shí)般哼,會(huì)產(chǎn)生一個(gè)前導(dǎo)4個(gè)0的散列吴汪。
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: <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):
"""
Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?
: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"
為了調(diào)整算法的難度漾橙,可以修改前導(dǎo)零的個(gè)數(shù)楞卡。但是4就足夠了。您將發(fā)現(xiàn)觉渴,添加一個(gè)前導(dǎo)零會(huì)大大縮短找到解決方案所需的時(shí)間徽惋。
我們的類幾乎完成了,我們已經(jīng)準(zhǔn)備好開始使用HTTP請(qǐng)求與它進(jìn)行交互踢京。