區(qū)塊鏈齊步走
想要理解區(qū)塊鏈如何工作最好的方式就是制作一個啦沛申。
Learn Blockchains by Building One
加密貨幣與區(qū)塊鏈(三):什么是信任
Keywords
ruby, blockchain, consensus
A
作為一個匪菜大軍中的一員铁材,不了解區(qū)塊鏈是不能稱為一個合格的匪菜的奕锌≈酰空氣幣的火熱,讓更多的匪菜充滿了渴望惊暴,似乎我們也需要了解一些在這個背后的基礎(chǔ)饼丘。了解這個其實并不是很簡單,因為更多的匪菜喜歡看到的是綠色的漲幅辽话,而不是背后的技術(shù)肄鸽,你可以在得(不)道上面找到很多奇怪的Talk,但是你的確得不到油啤。
我喜歡邊做邊學典徘,看完下面的例子,我相信你能成為一顆不一樣的匪菜逮诲。
B
區(qū)塊鏈(Blockchain),顧名思義就是由塊組成的鏈,每一個塊就是一些數(shù)據(jù)梅鹦,然后通過一種手法把這個塊連接起來裆甩,就是用一個哈希函數(shù) H,把block B[i]的哈希值 H(B[i]) 包含在下一個 block B[i+1] 里齐唆。H 具有單向性淑掌,也就是知道 B 就很容易算出 H(B),但是反過來如果只知道 H(B) 的值很難構(gòu)造出一個滿足條件的 B蝶念。當然啦,這個其實就是一個鏈表芋绸,POC媒殉。這樣做的結(jié)果就意味著如果其中任何一塊被修改了。而因為 H(B0) 是 B1 的一部分摔敛,所以導致 H(B1) 也要跟著變廷蓉。如果有人要修改記錄在這個鏈上的數(shù)據(jù),就需要修改后面所有的塊马昙。這個就叫做Merkle List桃犬。如果你用過Gayhub,那么你應該也知道行楞,Git存儲的方式就是基于Merkle List攒暇。
C
在你開始之前,我和你們說這篇教程使用的是Ruby語言寫的子房。這里用了一些很簡單的庫來幫助我們可以做一個簡單的Web Application形用,cuba
, faraday
。這里就不多做解釋了证杭。
STEP 1
在開始前田度,你可以在這里看到源代碼傳送門
我們在這里創(chuàng)建一個Blockchain的Blueprint
class Blockchain
end
Emmmm, that was a joke.
class Blockchain
class << self
def hash blk
end
end
def initialize
end
def new_block
end
def new_transacction
end
def last_block
end
end
我們的Blockchain是用來對鏈初始化,然后添加一些常用的操作的解愤,new_block, new_transaction, hash等镇饺。
那么一個Block應該是什么樣子的呢?
block = {
'index': 1,
'timestamp': 1506057125,
'transactions': [
{
'sender': "8527147fe1f5426f9dd545de4b27ee00",
'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",
'amount': 5,
}
],
'proof': 324984774000,
'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
}
接下來我們需要創(chuàng)建新的塊了送讲,在我們的Blockchain初始化的時候奸笤,我們需要給他一個創(chuàng)世塊,一個沒有祖先的塊李茫,同時我們也需要給創(chuàng)世塊增加一個 proof
揭保,這是挖礦的結(jié)果,我稍后再說啦魄宏。
我們現(xiàn)在需要了解什么是 PoW (Proof of Work)秸侣,顧名思義就是新的區(qū)塊是如何產(chǎn)生或者如何被挖出來的,它存在的目的就是發(fā)現(xiàn)能夠解決一個問題的數(shù)字,這個數(shù)字需要具備兩個屬性味榛,難找和易驗證椭坚。
我們舉一個簡單的例子
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}')
那么結(jié)果就是21,在比特幣中搏色,PoW的算法叫做Hashcash善茎,和上面的例子是差不多的,礦工們算出結(jié)果之后是會被獎賞的频轿,礦工會在一個交易中收到一個幣垂涯。
PoW算法是很簡單的,那么我們現(xiàn)在的題目就是:
找到一個數(shù)字p航邢,使得hash(pp')的結(jié)果包含是由4個0開頭的耕赘。這里p代表之前的proof,p'是新的proof
...
def PoW(last_proof)
# proof of work algorithm (PoW)
proof = 0
while valid_proof?(last_proof, proof) == false
proof += 1
end
proof
end
...
private
def valid_proof?(last_proof, proof)
Digest::SHA256.hexdigest("#{last_proof}#{proof}")[0..3] == "0000"
end
如果你需要修改算法的難度膳殷,那么你只需要修改以0開頭的個數(shù)就可以了操骡。
STEP 2
我們這里用 cuba
做一個很小的 web 服務(wù),它主要包含了三個功能
- POST /transactions/new 生成一筆新的交易
- GET /mine 告訴服務(wù)器產(chǎn)生一個新的塊
- GET /chain 把當前鏈返回
node_identifier = SecureRandom.hex(20)
blockchain = Blockchain.new
Cuba.define do
on get do
on 'mine' do
last_block = blockchain.last_block
last_proof = last_block[:proof]
proof = blockchain.PoW(last_proof)
blockchain.new_transaction("0", node_identifier.to_s, 1)
previous_hash = Blockchain.hash(last_block)
blk = blockchain.new_block(proof, previous_hash)
data = {
message: 'new block forged',
index: blk[:index],
transactions: blk[:transactions],
proof: blk[:proof],
previous_hash: blk[:previous_hash]
}
as_json {{ data: data }}
end
on 'chain' do
as_json do
{
data: {
chain: blockchain.chain,
length: blockchain.chain.count
}
}
end
end
end
on post do
on 'transactions/new' do
on param('sender'), param('recipient'), param('amount') do |sender, recipient, amount|
index = blockchain.new_transaction(sender,recipient, amount.to_f)
as_json {{ data: "transaction will be added to block #{index}"}}
end
on true do
as_json 400 do
{ error: 'missing params'}
end
end
end
end
end
接下來就可以跑一跑我們的簡單的服務(wù)器啦
thin start -p 3000
你可以使用 Postman 或者 curl 來調(diào)用我們的服務(wù)赚窃。
STEP 3
共識册招,這個很重要,在分布式系統(tǒng)中勒极,你需要保證數(shù)據(jù)的一致性是掰,所以你需要知道我們需要通過一種什么樣的算法來保證我們始終指向一條鏈。
- POST /nodes/register 我們把當前網(wǎng)絡(luò)的節(jié)點都存到一個地方
- GET /nodes/resolve 這個地方用來解決沖突
def valid_chain?(chain)
last_block = chain[0]
current_index = 1
while current_index < chain.size
block = chain[current_index]
puts "count 1"
# Check that the hash of the block is correct
if block[:previous_hash] != Blockchain.hash(last_block)
return false
end
# Check that the Proof of Work is correct
if !valid_proof?(last_block[:proof], block[:proof])
return false
end
last_block = block
current_index += 1
end
return true
end
def resolve_conflicts
new_chain = nil
# Only looking for chains longer than this one
max_length = @chain.count
aval = @nodes.delete @current_node
aval.each do |node|
conn = Faraday.new(url: "http://#{node}/chain")
res = conn.get do |conn_get|
conn_get.options.open_timeout = 15
conn_get.options.timeout = 15
end
if res.status == 200
content = JSON.parse(res.body, symbolize_names: true)
length = content[:data][:length]
chain = content[:data][:chain]
ap "node #{node} len #{length > max_length} valid_chain #{valid_chain?(chain)}"
if length > max_length && valid_chain?(chain)
max_length = length
new_chain = chain
end
end
end
if new_chain
puts "found new chain here"
@chain = new_chain
return true
end
return false
end
# in server.rb
on 'nodes/resolve' do
blockchain.current_node = "#{env["SERVER_NAME"]}:#{env["SERVER_PORT"]}"
resolved = blockchain.resolve_conflicts
if resolved
data = {
message: "our chain was replaced",
new_chain: blockchain.chain
}
else
data = {
message: "our chain was authorized",
new_chain: blockchain.chain
}
end
as_json {{ data: data }}
end
valid_chain? 用來判斷這個鏈是否正確的河质,可以看到 resolve_conflicts 里面主要用到的判斷就是鏈長和是否是一條合格的鏈冀惭,如果更長并且合法,那么當前的鏈就替換為該鏈掀鹅。不過這里值得注意的是散休,你需要把nodes中的自己的節(jié)點移除掉,這樣其實是可以提高速度乐尊,并可以解決一個問題戚丸,單線程的機器中,你不能call自己扔嵌。(會有一個OpenTimeOUt的錯誤限府,總是這里你把自己本身的節(jié)點去掉就可以了。)
到這里痢缎,你就可以用另一臺機器或者使用不同的端口來模擬多個節(jié)點胁勺,我這邊 Rakefile 里面默認是通過單機不同端口來模擬多個節(jié)點。
完整的代碼請參考傳送門
你可以開始和小伙伴們玩耍這個蠢蠢的鏈了
希望這個東西能夠給你一些啟發(fā)独旷。
當然啦署穗,最后送上一句話寥裂。
如果你認為技術(shù)能解決安全問題,那么你既不懂安全也不懂技術(shù)案疲。 :)
有很多關(guān)于區(qū)塊鏈的文章都說「區(qū)塊鏈解決的核心問題是信任問題」封恰,但是我沒有看到有人回答了關(guān)鍵的問題:到底什么是信任?什么是所謂「信任問題」褐啡,它存不存在诺舔?什么算是「解決了信任問題」?事實上如果在 Google 上搜一下這句話备畦,會找到大量的復制粘貼和人云亦云低飒。Brice Schneier 書里那句話改一改也是適用的,如果有人認為技術(shù)能解決信任問題懂盐,那么他恐怕既不懂信任也不懂技術(shù)逸嘀。