虽然有些人认为区块链是一个迟早会出现问题的解决方案,可是毫无疑问,这个创新技术是一个计算机技术上的奇迹。那么,究竟什么是区块链呢?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 了。