雖然有些人認爲區塊鏈是一個遲早會出現問題的解決方案,可是毫無疑問,這個創新技術是一個計算機技術上的奇蹟。那麼,究竟什麼是區塊鏈呢?html
以比特幣(Bitcoin)或其它加密貨幣按時間順序公開地記錄交易的數字帳本。node
更通俗的說,它是一個公開的數據庫,新的數據存儲在被稱之爲區塊(block)的容器中,並被添加到一個不可變的鏈(chain)中(所以被稱爲區塊鏈(blockchain)),以前添加的數據也在該鏈中。對於比特幣或其它加密貨幣來講,這些數據就是一組組交易,不過,也能夠是其它任何類型的數據。git
區塊鏈技術帶來了全新的、徹底數字化的貨幣,如比特幣和萊特幣(Litecoin),它們並不禁任何中心機構管理。這給那些認爲當今的銀行系統是騙局並將最終走向失敗的人帶來了自由。區塊鏈也革命性地改變了分佈式計算的技術形式,如以太坊(Ethereum)就引入了一種有趣的概念:智能合約(smart contract)。github
在這篇文章中,我將用不到 50 行的 Python 2.x 代碼實現一個簡單的區塊鏈,我把它叫作 SnakeCoin。算法
咱們首先將從定義咱們的區塊是什麼開始。在區塊鏈中,每一個區塊隨同時間戳及可選的索引一同存儲。在 SnakeCoin 中,咱們會存儲這二者。爲了確保整個區塊鏈的完整性,每一個區塊都會有一個自識別的哈希值。如在比特幣中,每一個區塊的哈希是該塊的索引、時間戳、數據和前一個區塊的哈希值等數據的加密哈希值。這裏說起的「數據」能夠是任何你想要的數據。數據庫
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import hashlib as hasher
class Block:
def __init__(self, index, timestamp, data, previous_hash):
self.index = index
self.timestamp = timestamp
self.data = data
self.previous_hash = previous_hash
self.hash = self.hash_block()
def hash_block(self):
sha = hasher.sha256()
sha.update(str(self.index) +
str(self.timestamp) +
str(self.data) +
str(self.previous_hash))
return sha.hexdigest()
|
真棒,如今咱們有了區塊的結構了,不過咱們須要建立的是一個區塊鏈。咱們須要把區塊添加到一個實際的鏈中。如咱們以前提到過的,每一個區塊都須要前一個區塊的信息。但問題是,該區塊鏈中的第一個區塊在哪裏?好吧,這個第一個區塊,也稱之爲創世區塊,是一個特別的區塊。在不少狀況下,它是手工添加的,或經過獨特的邏輯添加的。json
咱們將建立一個函數來簡單地返回一個創世區塊解決這個問題。這個區塊的索引爲 0 ,其包含一些任意的數據值,其「前一哈希值」參數也是任意值。flask
1
2
3
4
5
6
|
import datetime as date
def create_genesis_block():
# Manually construct a block with
# index zero and arbitrary previous hash
return Block(0, date.datetime.now(), "Genesis Block", "0")
|
如今咱們能夠建立創世區塊了,咱們須要一個函數來生成該區塊鏈中的後繼區塊。該函數將獲取鏈中的前一個區塊做爲參數,爲要生成的區塊建立數據,並用相應的數據返回新的區塊。新的區塊的哈希值來自於以前的區塊,這樣每一個新的區塊都提高了該區塊鏈的完整性。若是咱們不這樣作,外部參與者就很容易「改變過去」,把咱們的鏈替換爲他們的新鏈了。這個哈希鏈起到了加密的證實做用,並有助於確保一旦一個區塊被添加到鏈中,就不能被替換或移除。服務器
1
2
3
4
5
6
|
def next_block(last_block):
this_index = last_block.index + 1
this_timestamp = date.datetime.now()
this_data = "Hey! I'm block " + str(this_index)
this_hash = last_block.hash
return Block(this_index, this_timestamp, this_data, this_hash)
|
這就是主要的部分。網絡
如今咱們能建立本身的區塊鏈了!在這裏,這個區塊鏈是一個簡單的 Python 列表。其第一個的元素是咱們的創世區塊,咱們會添加後繼區塊。由於 SnakeCoin 是一個極小的區塊鏈,咱們僅僅添加了 20 個區塊。咱們經過循環來完成它。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# Create the blockchain and add the genesis block
blockchain = [create_genesis_block()]
previous_block = blockchain[0]
# How many blocks should we add to the chain
# after the genesis block
num_of_blocks_to_add = 20
# Add blocks to the chain
for i in range(0, num_of_blocks_to_add):
block_to_add = next_block(previous_block)
blockchain.append(block_to_add)
previous_block = block_to_add
# Tell everyone about it!
print "Block #{} has been added to the blockchain!".format(block_to_add.index)
print "Hash: {}n".format(block_to_add.hash)
|
讓咱們看看咱們的成果:
別擔憂,它將一直添加到 20 個區塊
很好,咱們的區塊鏈能夠工做了。若是你想要在主控臺查看更多的信息,你能夠編輯其完整的源代碼並輸出每一個區塊的時間戳或數據。
這就是 SnakeCoin 所具備的功能。要使 SnakeCoin 達到現今的產品級的區塊鏈的高度,咱們須要添加更多的功能,如服務器層,以在多臺機器上跟蹤鏈的改變,並經過工做量證實算法(POW)來限制給定時間週期內能夠添加的區塊數量。
若是你想了解更多技術細節,你能夠在這裏查看最初的比特幣白皮書。
這個極小的區塊鏈及其簡單,天然也相對容易完成。可是因其簡單也帶來了一些缺陷。首先,SnakeCoin 僅能運行在單一的一臺機器上,因此它相距分佈式甚遠,更別提去中心化了。其次,區塊添加到區塊鏈中的速度同在主機上建立一個 Python 對象並添加到列表中同樣快。在咱們的這個簡單的區塊鏈中,這不是問題,可是若是咱們想讓 SnakeCoin 成爲一個實際的加密貨幣,咱們就須要控制在給定時間內能建立的區塊(和幣)的數量。
從如今開始,SnakeCoin 中的「數據」將是交易數據,每一個區塊的「數據」字段都將是一些交易信息的列表。接着咱們來定義「交易」。每一個「交易」是一個 JSON 對象,其記錄了幣的發送者、接收者和轉移的 SnakeCoin 數量。注:交易信息是 JSON 格式,緣由我很快就會說明。
1
2
3
4
5
|
{
"from": "71238uqirbfh894-random-public-key-a-alkjdflakjfewn204ij",
"to": "93j4ivnqiopvh43-random-public-key-b-qjrgvnoeirbnferinfo",
"amount": 3
}
|
如今咱們知道了交易信息看起來的樣子了,咱們須要一個辦法來將其加到咱們的區塊鏈網絡中的一臺計算機(稱之爲節點)中。要作這個事情,咱們會建立一個簡單的 HTTP 服務器,以便每一個用戶均可以讓咱們的節點知道發生了新的交易。節點能夠接受 POST 請求,請求數據爲如上的交易信息。這就是爲何交易信息是 JSON 格式的:咱們須要它們能夠放在請求信息中傳遞給服務器。
1
|
$ pip install flask # 首先安裝 Web 服務器框架
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
from flask import Flask
from flask import request
node = Flask(__name__)
# Store the transactions that
# this node has in a list
this_nodes_transactions = []
@node.route('/txion', methods=['POST'])
def transaction():
if request.method == 'POST':
# On each new POST request,
# we extract the transaction data
new_txion = request.get_json()
# Then we add the transaction to our list
this_nodes_transactions.append(new_txion)
# Because the transaction was successfully
# submitted, we log it to our console
print "New transaction"
print "FROM: {}".format(new_txion['from'])
print "TO: {}".format(new_txion['to'])
print "AMOUNT: {}\n".format(new_txion['amount'])
# Then we let the client know it worked out
return "Transaction submission successful\n"
node.run()
|
真棒!如今咱們有了一種保存用戶彼此發送 SnakeCoin 的記錄的方式。這就是爲何人們將區塊鏈稱之爲公共的、分佈式帳本:全部的交易信息存儲給全部人看,並被存儲在該網絡的每一個節點上。
可是,有個問題:人們從哪裏獲得 SnakeCoin 呢?如今尚未辦法獲得,尚未一個稱之爲 SnakeCoin 這樣的東西,由於咱們尚未建立和分發任何一個幣。要建立新的幣,人們須要「挖」一個新的 SnakeCoin 區塊。當他們成功地挖到了新區塊,就會建立出一個新的 SnakeCoin ,並獎勵給挖出該區塊的人(礦工)。一旦挖礦的礦工將 SnakeCoin 發送給別人,這個幣就流通起來了。
咱們不想讓挖新的 SnakeCoin 區塊太容易,由於這將致使 SnakeCoin 太多了,其價值就變低了;一樣,咱們也不想讓它變得太難,由於若是沒有足夠的幣供每一個人使用,它們對於咱們來講就太昂貴了。爲了控制挖新的 SnakeCoin 區塊的難度,咱們會實現一個工做量證實(Proof-of-Work)(PoW)算法。工做量證實基本上就是一個生成某個項目比較難,可是容易驗證(其正確性)的算法。這個項目被稱之爲「證實」,聽起來就像是它證實了計算機執行了特定的工做量。
在 SnakeCoin 中,咱們建立了一個簡單的 PoW 算法。要建立一個新區塊,礦工的計算機須要遞增一個數字,當該數字能被 9 (「SnakeCoin」 這個單詞的字母數)整除時,這就是最後這個區塊的證實數字,就會挖出一個新的 SnakeCoin 區塊,而該礦工就會獲得一個新的 SnakeCoin。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
# ...blockchain
# ...Block class definition
miner_address = "q3nf394hjg-random-miner-address-34nf3i4nflkn3oi"
def proof_of_work(last_proof):
# Create a variable that we will use to find
# our next proof of work
incrementor = last_proof + 1
# Keep incrementing the incrementor until
# it's equal to a number divisible by 9
# and the proof of work of the previous
# block in the chain
while not (incrementor % 9 == 0 and incrementor % last_proof == 0):
incrementor += 1
# Once that number is found,
# we can return it as a proof
# of our work
return incrementor
@node.route('/mine', methods = ['GET'])
def mine():
# Get the last proof of work
last_block = blockchain[len(blockchain) - 1]
last_proof = last_block.data['proof-of-work']
# Find the proof of work for
# the current block being mined
# Note: The program will hang here until a new
# proof of work is found
proof = proof_of_work(last_proof)
# Once we find a valid proof of work,
# we know we can mine a block so
# we reward the miner by adding a transaction
this_nodes_transactions.append(
{ "from": "network", "to": miner_address, "amount": 1 }
)
# Now we can gather the data needed
# to create the new block
new_block_data = {
"proof-of-work": proof,
"transactions": list(this_nodes_transactions)
}
new_block_index = last_block.index + 1
new_block_timestamp = this_timestamp = date.datetime.now()
last_block_hash = last_block.hash
# Empty transaction list
this_nodes_transactions[:] = []
# Now create the
# new block!
mined_block = Block(
new_block_index,
new_block_timestamp,
new_block_data,
last_block_hash
)
blockchain.append(mined_block)
# Let the client know we mined a block
return json.dumps({
"index": new_block_index,
"timestamp": str(new_block_timestamp),
"data": new_block_data,
"hash": last_block_hash
}) + "\n"
|
如今,咱們能控制特定的時間段內挖到的區塊數量,而且咱們給了網絡中的人新的幣,讓他們彼此發送。可是如咱們說的,咱們只是在一臺計算機上作的。若是區塊鏈是去中心化的,咱們怎樣才能確保每一個節點都有相同的鏈呢?要作到這一點,咱們會使每一個節點都廣播其(保存的)鏈的版本,並容許它們接受其它節點的鏈。而後,每一個節點會校驗其它節點的鏈,以便網絡中每一個節點都可以達成最終的鏈的共識。這稱之爲共識算法(consensus algorithm)。
咱們的共識算法很簡單:若是一個節點的鏈與其它的節點的不一樣(例若有衝突),那麼最長的鏈保留,更短的鏈會被刪除。若是咱們網絡上的鏈沒有了衝突,那麼就能夠繼續了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
@node.route('/blocks', methods=['GET'])
def get_blocks():
chain_to_send = blockchain
# Convert our blocks into dictionaries
# so we can send them as json objects later
for block in chain_to_send:
block_index = str(block.index)
block_timestamp = str(block.timestamp)
block_data = str(block.data)
block_hash = block.hash
block = {
"index": block_index,
"timestamp": block_timestamp,
"data": block_data,
"hash": block_hash
}
# Send our chain to whomever requested it
chain_to_send = json.dumps(chain_to_send)
return chain_to_send
def find_new_chains():
# Get the blockchains of every
# other node
other_chains = []
for node_url in peer_nodes:
# Get their chains using a GET request
block = requests.get(node_url + "/blocks").content
# Convert the JSON object to a Python dictionary
block = json.loads(block)
# Add it to our list
other_chains.append(block)
return other_chains
def consensus():
# Get the blocks from other nodes
other_chains = find_new_chains()
# If our chain isn't longest,
# then we store the longest chain
longest_chain = blockchain
for chain in other_chains:
if len(longest_chain) < len(chain):
longest_chain = chain
# If the longest chain wasn't ours,
# then we set our chain to the longest
blockchain = longest_chain
|
咱們差很少就要完成了。在運行了完整的 SnakeCoin 服務器代碼以後,在你的終端能夠運行以下代碼。(假設你已經安裝了 cCUL)。
1
2
3
|
curl "localhost:5000/txion" \
-H "Content-Type: application/json" \
-d '{"from": "akjflw", "to":"fjlakdj", "amount": 3}'
|
1
|
curl localhost:5000/mine
|
對代碼作下美化處理,咱們看到挖礦後咱們獲得的新區塊的信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
{
"index": 2,
"data": {
"transactions": [
{
"to": "fjlakdj",
"amount": 3,
"from": "akjflw"
},
{
"to": "q3nf394hjg-random-miner-address-34nf3i4nflkn3oi",
"amount": 1,
"from": "network"
}
],
"proof-of-work": 36
},
"hash": "151edd3ef6af2e7eb8272245cb8ea91b4ecfc3e60af22d8518ef0bba8b4a6b18",
"timestamp": "2017-07-23 11:23:10.140996"
}
|
大功告成!如今 SnakeCoin 能夠運行在多個機器上,從而建立了一個網絡,並且真實的 SnakeCoin 也能被挖到了。
你能夠根據你的喜愛去修改 SnakeCoin 服務器代碼,並問各類問題了。
在下一篇(LCTT 譯註:截止至本文翻譯,做者尚未寫出下一篇),咱們將討論建立一個 SnakeCoin 錢包,這樣用戶就能夠發送、接收和存儲他們的 SnakeCoin 了。