一個 Ruby 實現的 Blockchain 導讀
Blockchain
是一種革命性的技術, 有些抽象, 比較複雜. 自從比特幣誕生以來, 有很多技術的科普文章, 甚至是製精良的動畫, 來解釋其中的原理. 其中不乏精品之作.
在區塊鏈的世界, 閱讀理論的作用是有限的. 離開實踐, 力有不逮, 心有餘而力不逮, 是很常見的. 從白皮書到代碼實現是一個飛躍, 需要一些耐心和技術儲備.
積累 Blockchain
的設計細節, 掌握某種編程語言是需要時間的, 適合的學習材料會縮短這個過程. 並且讓你妥帖的撫摸區塊的皮膚, 感受其鍊式形體.
另一方面, 一旦製作出一個完整的 Blockchain
的 Demo, 這項技術瞬間與你的靈魂結合, 留下虔誠的烙印和神聖的宗教代碼.
2017年末, 知名的 Blockchainist
, 利他主義猛士 Haseeb Qureshi 先生, 發布了一個 Blockchain
Demo 視頻及代碼, 這是一場兩個小時的儀式, 是賽博朋克洗禮的聖水.
對 Blockchainist
而言, 若未曾實現一個小小的 Demo, 猶如應和了一句古話: 為人不識陳近南,就稱英雄也枉然。
學習這個小項目, 猶如在你修煉區塊神功時, 將 Haseeb 的剛猛內力傳入你體內, 雖可以加速修煉進度, 但也存在水土不服, 心情煩躁, 走火入魔的風險.
當你吸入, 消化洋人內力時, 需要本尊這粒藥引子, 去腥除騷, 護體保平安.
下面逐層介紹神功的實現目標以及必備知識.
神功第一層
錢坤大挪移,
謝遜度假夏威夷
這一層實現查賬轉賬功能, 轉賬功能是根據賬戶餘額, 進行加減計算.
此類功能用 HTTP
協議的 GET
和 POST
實現最適合, GET
用來獲取服務端數據, POST
用來更改服務端數據.
這裡不需要 HTML
來呈現 UI, 使用 Ruby Web 框架 Sinatra
來組織 URL
和相關的 method
, 在命令行終端可以看到轉賬信息.
客戶端的 method
和服務端的 url
都非常簡潔
客戶端: client.rb
def create_user(name) … end def get_balance(user) … end def transfer(from, to, amount) ... end
服務端: haseebcoin.rb
get “/balance” ... end post “/users” ... end post “/transfers” ... end
本層必要知識: ruby
, HTTP
GET
POST
, Sinatra
神功第二層
白玉堂大破銅網陣
姦王私建沖霄樓, 銅網陣, 裡面遍布是消息埋伏, 探測環境變化, 傳遞信息, 觸動奪命機關.
中描述了一種致命的物聯網, 區塊鏈中也有類似的結構, Gossip Protocols
八卦, 不是乾, 坤, 坎, 離… 是八卦新聞的意思, 是去中心化網絡.
我們建立一個能夠交換電影名稱的 Gossip
網絡.
client.rb 實現了向某端口送消息
def self .gossip(port, state)
...
Faraday .post( " #{ URL } : #{port} /gossip" , state: state).body ...
end
gossip.rb
接受兩個參數, 源端口和目的端口, 源端口在某個端口說話, 比如端口 1111
, 2222
.
在實際的去中心網絡中, 這倆端口代表兩個網絡節點, 在本機上不同端口說話, 模擬網絡中的不同的節點.
每個節點
每 3
秒, 說出最愛電影名稱
every( 3 .seconds) do …
gossip_response = Client .gossip(port, JSON .dump( STATE ))
update_state( JSON .load(gossip_response))
... end
每 8 秒, 改變最愛電影.
every( 8 .seconds) do …
update_state( PORT => [ @favorite_movie , @version_number ])
... end
服務端接收並處理數據
post '/gossip' do …
update_state( JSON .load(their_state))
… end
在一個四人網絡中:
-
初始節點運行
gossip.rb 1111
, 第一節點在 1111 端口說出最愛電影 -
運行
gossip.rb 2222 1111
, 第二節點在 2222 端口向第一節點 (1111 端口) 說出最愛電影 -
運行
gossip.rb 3333 2222
, 第三節點在 3333 端口向第二節點 (2222 端口) 說出最愛電影 -
運行
gossip.rb 4444 3333
, 第四節點在 4444 端口向第三節點 (3333 端口) 說出最愛電影
運行一段時間後, 最終, 四個節點都有對方的信息, 而且信息在不停的變化. 這就是一個簡單的 Gossip
網絡.
神功第三層
倚天屠龍藏玄機,
武穆遺書加解密
完顏洪烈道:“岳飛無法可施,只得把那部兵書貼身藏了,寫了四首甚麼《菩薩蠻》、《醜奴兒》、《賀聖朝》、《齊天樂》的歪詞。
這四首詞格律不對,平仄不葉,句子顛三倒四,不知所云。 ”
“哪知其中竟是藏著一個極大的啞謎。
小王苦苦思索,終於解明了,原來這四首歪詞須得每隔三字的串讀,先倒後順,反复連貫,便即明明白白。 ”
頂級加密算法是區塊鏈的基石.
這一層使用非對稱加密技術, 實現區塊鏈賬戶.
RSA
算法能夠生成公鑰, 私鑰, 並實現非對稱加密功能.
def generate_key_pair … end
def sign(plaintext, raw_private_key) ... end
得益於 Ruby
語言的 openssl module
, 我們可以很輕鬆的實現非對稱加密和簽名驗證等功能.
在區塊鏈中, 公鑰是用戶名, 私鑰是密碼, 一對密鑰, 就是一個區塊鏈賬戶.
解密 ciphertext:
def plaintext(ciphertext, raw_public_key) … end
驗證 ciphertext 是不是 message:
def valid_signature?(message, ciphertext, public_key) … end
本層必要知識: 非對稱加密算法
神功第四層
老君爐裡賴一年,
釀製區塊出金丹
這一層實現了工作證明, 產生了區塊鏈中的區塊, 是費時費力的過程. Hash Function
的特點是不可逆和無衝突, 計算過程很簡單, 將 input
經過哈希運算, 得到 result
, input
是轉賬, 錢, 花錢的人, 收錢的人等等信息
哈希運算有很多種算法, 這裡使用 SHA256
算法:
def hash(message) … end
同樣的信息, 做同樣的哈希運算, 會得到不同的 result, 我們不停的做運算, 直到得到的 result 符合某些特性, 比如 result 前幾位都是 0.
驗證運算結果是不是以幾個 0 開始:
def is_valid_nonce?(nonce, message)
hash(message + nonce).start_with?( "0" * NUM_ZEROES ) end
符合以上條件運算執行起來不是那麼容易, 需要耗費大量時間, 整個過程被稱為挖礦:
def find_nonce(message)
… until is_valid_nonce?(nonce, message)
...
end
input
中會包含上一次哈希運算的結果, 所以每次哈希運算都收到了上一次運算的影響, 換句話說, 這是一個鍊式結構, 也就是區塊鏈的由來.
神功第五層
區塊鏈節節扣,
最長鏈掃乾坤
這一層會初始化第一個區塊, 並據此, 生產一個鍊式結構, 形成區塊鏈. 區塊鏈可以存儲在 Array
結構中, 在存儲的過程中, 還要驗證區塊是否有效.
初始化區塊 class Block
def initialize(prev_block, msg) @msg = msg
@prev_block_hash = prev_block.own_hash if prev_block
mine_block!
end
挖礦, 最繁重的勞動是找 nonce
def mine_block! @nonce = calc_nonce
@own_hash = hash(full_block( @nonce ))
end
一個完整的區塊是這樣 compact
出來的
def full_block(nonce)
[ @msg , @prev_block_hash , nonce].compact.join end
初始化區塊鏈 class BlockChain
用 Array
存儲就可以啦:
def initialize(msg)
@blocks = [] @blocks Block.new( nil , msg)
end
將區塊加入鏈條, 整個區塊鏈在不停的增長
def add_to_chain(msg) @blocks Block.new( @blocks .last, msg)
puts @blocks .last
end
一定要嚴格的驗證 block
是不是健康
def valid? @blocks .all? { |block| block.is_a?( Block ) } &&
@blocks .all?(& :valid? ) &&
@blocks .each_cons( 2 ).all? { |a, b| a.own_hash == b.prev_block_hash }
end
神功第六層
六合之法, 融會貫通
朋克神教, 初現神通
所謂六合,“精氣神”為內三合,“手眼身”為外三合,
其用為“眼與心合,心與氣 合,氣與身合,身與手合,手與腳合,腳與胯合。”
全身內外,渾然一體。
此乃少林旁支韋陀門的武功,全守六合之法。
在第一層轉賬交易 class Transaction
中, 需要用私鑰對信息進行簽名
@signature = PKI .sign(message, priv_key)
第一個挖出區塊, 會的到 500_000 大洋的獎勵.
def self .create_genesis_block(pub_key, priv_key)
genesis_txn = Transaction .new( nil , pub_key, 500_000 , priv_key) Block .new( nil , genesis_txn)
end
驗證賬戶花錢是不是有效
def all_spends_valid?
compute_balances do |balances, from, to| return false if balances.values_at(from, to).any? { |bal| bal 0 }
end true
end
將未知的節點加入 $PEERS
, 保持網絡增長
if PEER_PORT . nil ? # You are the progenitor! $BLOCKCHAIN = BlockChain .new( PUB_KEY , PRIV_KEY )
else # You're just joining the network. $PEERS PEER_PORT
end
節點之間的處理數據, 先讀取 blockchain
和 peers
, 然後更新他們
# @param blockchain # @param peers post '/gossip' do their_blockchain = YAML .load(params[ 'blockchain' ])
their_peers = YAML .load(params[ 'peers' ])
update_blockchain(their_blockchain)
update_peers(their_peers) YAML .dump( 'peers' => $PEERS , 'blockchain' => $BLOCKCHAIN )
end
處理接受到的區塊, 我們只關心他是不是更長
def update_blockchain(their_blockchain) return if their_blockchain. nil ?
return if $BLOCKCHAIN && their_blockchain.length $BLOCKCHAIN.length
return unless their_blockchain.valid? $BLOCKCHAIN = their_blockchain
end
更新 peers
, 只要以前沒有的 peer
:
def update_peers(their_peers) $PEERS = ( $PEERS + their_peers).uniq
end
發送錢, 需要先得到對方的 pub_key
, 然後從我的 pub_key
向他發送 amount
.
# @param to (port_number) # @param amount post '/send_money' do to = Client .get_pub_key(params[ 'to' ])
amount = params[ 'amount' ].to_i $BLOCKCHAIN .add_to_chain( Transaction .new( PUB_KEY , to, amount, PRIV_KEY ))
'OK. Block mined!'
end
區塊鏈放進 Gossip
網絡, 將各個功能組合到一起, 一個可運行的 Block Demo
就成功了.
這個 Demo
在 Github
上, 油管上配有視頻,
https://github.com/Haseeb-Qureshi/lets-build-a-blockchain
最後祝大家練功順利, 早日大成.
推薦
—
知識星球
待字閨中官方區塊鏈知識星球,已經有接近500人加入星球,期待你的加入。 一起討論區塊鏈、數字貨幣。 做區塊鏈社區中的精品良心社區。
往期推薦: